写点什么

Android _《看完不忘系列》之 Retrofit,flutter 下载文件

用户头像
Android架构
关注
发布于: 刚刚
  • 源码级:手动编写代理类、APT 生成代理类

  • 字节码级:编译期生成字节码、


举个栗子,



interface 赚钱 {void makeMoney(int income);}


class 小鲜肉 implements 赚钱 { //委托类 @Overridepublic void makeMoney(int income) {System.out.println("开拍,赚个" + income);}}


class 经纪人 implements 赚钱 { //代理类赚钱 xxr;


public 经纪人(赚钱 xxr) {this.xxr = xxr;}


@Overridepublic void makeMoney(int income) {if (income < 1000_0000) { //控制访问 System.out.println("才" + income + ",先回去等通知吧");} else {xxr.makeMoney(income);}}}


public static void main(String[] args) {赚钱 xxr = new 小鲜肉();赚钱 jjr = new 经纪人(xxr);jjr.makeMoney(100_0000); //输出:才 1000000,先回去等通知吧 jjr.makeMoney(1000_0000); //输出:开拍,赚个 1000000


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


0}


为什么代理类委托类要实现相同接口?是为了尽可能保证代理类的内部结构和委托类一致,这样对代理类的操作都可以转移到委托类上,代理类只关注增强控制

动态代理

运行期生成字节码,如 Proxy.newProxyInstance、CGLIB


Proxy.newProxyInstance 是 java 自带,只能对接口代理(因为生成的类已经继承了 Proxy,java 没法多继承)

CGLIB 则更强大,还能对普通类代理,底层基于 ASM(ASM 使用类似SAX解析器逐行扫描来提高性能)


举个栗子,



class 合作标准 implements InvocationHandler {赚钱 xxr;


public 合作标准(赚钱 xxr) {this.xxr = xxr;}


@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {int income = (int) args[0];if (income < 1000_0000) { //控制访问 System.out.println("才" + income + ",先回去等通知吧");return null;} else {return method.invoke(xxr, args);}}}


public static void main(String[] args) {赚钱 xxr = new 小鲜肉();合作标准 standard = new 合作标准(xxr);//生成类(字节码):class $Proxy0 extends Proxy implements 赚钱//然后反射创建其实例 bd,即来一场临时的商务拓展赚钱 bd = (赚钱) Proxy.newProxyInstance(赚钱.class.getClassLoader(),new Class[]{赚钱.class},standard);//调用 makeMoney,内部转发给了合作标准的 invokebd.makeMoney(100_0000);bd.makeMoney(1000_0000);}


通过栗子可以看出,动态代理不需要提前创建具体的代理类(如经纪人经纪公司)去实现赚钱接口,而是先拟一份合作标准(InvocationHandler),等到运行期才创建代理类$Proxy0(字节码),然后反射创建其实例商务拓展,这样显得更为灵活。


了解完动态代理,就可以开始 Retrofit 之旅了~

树干

简单使用

引入依赖,


implementation 'com.squareup.okhttp3:okhttp:3.14.9'implementation 'com.squareup.retrofit2:retrofit:2.9.0'implementation 'com.squareup.retrofit2:converter-gson:2.9.0'implementation 'com.google.code.gson:gson:2.8.6'


定义接口WanApi


interface WanApi {//用注解标记网络请求类型 get,参数 path@GET("article/list/{page}/json")Call<WanArticleBean> articleList(@Path("page") int page);}


发起请求,


class RetrofitActivity extends AppCompatActivity {final String SERVER = "https://www.xxx.com/";


@Overrideprotected void onCreate(Bundle savedInstanceState) {Retrofit retrofit = new Retrofit.Builder().baseUrl(SERVER) //指定服务器地址.addConverterFactory(GsonConverterFactory.create()) //用 gson 将数据反序列化成实体.build();//运行期生成一个实现 WanApi 接口的类(字节码),并反射创建其实例 WanApi wanApi = retrofit.create(WanApi.class);//得到 Retrofit 的 call,他封装了 okhttp 的 callCall<WanArticleBean> call = wanApi.articleList(0);//请求入队 call.enqueue(new Callback<WanArticleBean>() {@Overridepublic void onResponse(Call<WanArticleBean> call, Response<WanArticleBean> response) {//得到数据实体 WanArticleBean bean = response.body();//不同于 okhttp,Retrofit 已经用 Handler 帮我们切回主线程了 mBinding.tvResult.setText("" + bean.getData().getDatas().size());}


@Overridepublic void onFailure(Call<WanArticleBean> call, Throwable t) {}});}}

实现原理


由于 Retrofit 底层基于 okhttp,哈迪在《看完不忘系列》之okhttp已经对网络流程做了分析,所以本文忽略网络实现只关注 Retrofit 自身的一些处理,Retrofit 对象的构建就是简单的 builder 模式,我们直接看 create,


//Retrofit.javapublic <T> T create(final Class<T> service) {//验证 validateServiceInterface(service);return (T)//动态代理 Proxy.newProxyInstance(service.getClassLoader(), //类加载器 new Class<?>[] {service}, //一组接口 new InvocationHandler() {//判断 android 和 jvm 平台及其版本 private final Platform platform = Platform.get();


@Overridepublic Object invoke(Object proxy, Method method, Object[] args){//如果该方法是 Object 的方法,直接执行不用管 if (method.getDeclaringClass() == Object.class) {return method.invoke(this, args);}//isDefaultMethod:检查是否是 java8 开始支持的接口默认方法 return platform.isDefaultMethod(method)? platform.invokeDefaultMethod(method, service, proxy, args): loadServiceMethod(method).invoke(args); //我们关注这里}});}


Proxy.newProxyInstance 动态代理,运行期会生成一个类(字节码)如$ProxyN,实现传入的接口即WanApi,重写接口方法然后转发给 InvocationHandler 的 invoke,如下(伪代码),


class $ProxyN extends Proxy implements WanApi{Call<WanArticleBean> articleList(@Path("page") int page){//转发给 invocationHandlerinvocationHandler.invoke(this,method,args);}}


我们先看 validateServiceInterface 验证逻辑,


//Retrofit.javaprivate void validateServiceInterface(Class<?> service) {//检查:WanApi 不是接口就抛异常...//检查:WanApi 不能有泛型参数,不能实现其他接口...if (validateEagerly) { //是否进行严格检查,默认关闭 Platform platform = Platform.get();for (Method method : service.getDeclaredMethods()) { //遍历 WanApi 方法//不是默认方法,并且不是静态方法 if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {//把方法提前加载进来(检查下有没有问题)loadServiceMethod(method);}}}}


如果开了 validateEagerly,会一次性把接口WanApi的所有方法都检查一遍并加载进来,可以在 debug 模式下开启,提前发现错误写法,比如在 @GET 请求设置了 @Body 这种错误就会抛出异常:


java.lang.IllegalArgumentException: Non-body HTTP method cannot contain @Body.

loadServiceMethod

然后是loadServiceMethod(method).invoke(args),看名字可知是先找方法,然后执行,


//Retrofit.java//缓存,用了线程安全 ConcurrentHashMapfinal Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();


ServiceMethod<?> loadServiceMethod(Method method) {ServiceMethod<?> result = serviceMethodCache.get(method);//WanApi 的 articleList 方法已缓存,直接返回 if (result != null) return result;synchronized (serviceMethodCache) {result = serviceMethodCache.get(method);if (result == null) {//解析 articleList 的注解,创建 ServiceMethod 并缓存起来 result = ServiceMethod.parseAnnotations(this, method);serviceMethodCache.put(method, result);}}return result;}


跟进 ServiceMethod.parseAnnotations,


//ServiceMethod.javastatic <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {//1.RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);//检查:articleList 方法返回类型不能用通配符和 void...//2.return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);}


先看 1. RequestFactory.parseAnnotations,


//RequestFactory.javastatic RequestFactory parseAnnotations(Retrofit retrofit, Method method) {return new Builder(retrofit, method).build();}


class Builder {RequestFactory build() {//解析方法注解如 GETfor (Annotation annotation : methodAnnotations) {parseMethodAnnotation(annotation);}//省略各种检查...//解析参数注解如 Pathint parameterCount = parameterAnnotationsArray.length;parameterHandlers = new ParameterHandler<?>[parameterCount];for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {parameterHandlers[p] =parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);}//省略各种检查...return new RequestFactory(this);}}


得到 RequestFactory 后,看 2. HttpServiceMethod.parseAnnotations,HttpServiceMethod 负责适配和转换处理,将接口方法的调用调整为 HTTP 调用,


//HttpServiceMethod.java//ResponseT 响应类型如 WanArticleBean,ReturnT 返回类型如 Callstatic <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(Retrofit retrofit, Method method, RequestFactory requestFactory) {//省略 kotlin 协程逻辑...Annotation[] annotations = method.getAnnotations();//遍历找到合适的适配器 CallAdapter<ResponseT, ReturnT> callAdapter =createCallAdapter(retrofit, method, adapterType, annotations);//得到响应类型,如 WanArticleBeanType responseType = callAdapter.responseType();//遍历找到合适的转换器 Converter<ResponseBody, ResponseT> responseConverter =createResponseConverter(retrofit, method, responseType);okhttp3.Call.Factory callFactory = retrofit.callFactory;return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);}


可见最终返回了一个 CallAdapted,看到 CallAdapted,


//CallAdapted extends HttpServiceMethod extends ServiceMethodclass CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {private final CallAdapter<ResponseT, ReturnT> callAdapter;


CallAdapted(RequestFactory requestFactory,okhttp3.Call.Factory callFactory,Converter<ResponseBody, ResponseT> responseConverter,CallAdapter<ResponseT, ReturnT> callAdapter) {super(requestFactory, callFactory, responseConverter);this.callAdapter = callAdapter;}


@Overrideprotected ReturnT adapt(Call<ResponseT> call, Object[] args) {//适配器 return callAdapter.adapt(call);}}


那这个 CallAdapter 实例到底是谁呢,我们先回到 Retrofit.Builder,


//Retrofit.Builder.javapublic Retrofit build() {Executor callbackExecutor = this.callbackExecutor;//如果没设置线程池,则给 android 平台设置一个默认的 MainThreadExecutor(用 Handler 将回调切回主线程)if (callbackExecutor == null) {callbackExecutor = platform.defaultCallbackExecutor();}List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);//添加默认的 DefaultCallAdapterFactorycallAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));}


DefaultCallAdapterFactory 这个工厂创建具体的 CallAdapter 实例,


//DefaultCallAdapterFactory.javapublic CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);//如果指定了 SkipCallbackExecutor 注解,就表示不需要切回主线程 final Executor executor =Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)? null: callbackExecutor;return new CallAdapter<Object, Call<?>>() {@Overridepublic Type responseType() {return responseType;}


@Overridepublic Call<Object> adapt(Call<Object> call) {//默认情况下,返回用主线程池包装的 Call,他的 enqueue 会使用主线程池的 executereturn executor == null ? call : new ExecutorCallbackCall<>(executor, call);}};}

invoke

前边loadServiceMethod得到了 CallAdapted,然后执行 invoke,实现在父类 HttpServiceMethod 里,


//HttpServiceMethod.javafinal ReturnT invoke(Object[] args) {//终于见到 okhttp 了!Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);return adapt(call, args);}


class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {private final CallAdapter<ResponseT, ReturnT> callAdapter;


@Overrideprotected ReturnT adapt(Call<ResponseT> call, Object[] args) {//用前边得到的适配器,把 OkHttpCall 包成 ExecutorCallbackCallreturn callAdapter.adapt(call);}}

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android _《看完不忘系列》之Retrofit,flutter下载文件