网站建设业务平均工资,企业网络安全管理制度和应急预案,深圳外贸有限公司,阿里巴巴运营的工作内容众所周知#xff0c;Retrofit是OkHttp的封装#xff0c;APP对网络交互部分的实现基本上都是RxJavaRetrofitOkHttp架构#xff08;或协程RetrofitOkHttp#xff09;#xff0c;可以说#xff0c;Retrofit已经广为人知。本文主要介绍Retrofit主线源码实现机制#xff0c;及…众所周知Retrofit是OkHttp的封装APP对网络交互部分的实现基本上都是RxJavaRetrofitOkHttp架构或协程RetrofitOkHttp可以说Retrofit已经广为人知。本文主要介绍Retrofit主线源码实现机制及其所采用的设计模式所涉及到的思想等等。
OKHttp 的使用缺陷
这里说的使用缺陷是不那么便利的地方
1用户网络请求的接口配置繁琐尤其是需要配置复杂请求body请求头参数的时候
2数据解析过程需要用户手动拿到responsbody进行解析不能复用
3无法适配自动进行线程的切换
4万一我们的存在嵌套网络请求就会陷入“回调陷阱”。Retrofit主要解决的问题
在OkHttp使用如此繁琐的情况下更方便使用的Retrofit应运而生。其所解决的OkHttp的缺陷问题包含两方面
①请求前完成统一配置的网络请求头一致适配请求request。
②结果返回retrofit完成数据适配、线程切换。简单使用
本段文字仅讲述retrofit的基本用法如果对其用法以及掌握的老铁可以直接跳过查看源码解析部分。
1、添加依赖
implementation com.squareup.retrofit2:retrofit:2.9.0
implementation com.squareup.retrofit2:converter-gson:2.9.02、定义网络服务接口
添加依赖并同步后创建一个接口专门负责定义网络API。这里我们采用接口是用于
调试Github的开放API接口为api.github.com/users/octoc… 在请求后会获取Github用户Octocat的Repo列表 复制其数据用AS的GsonFormat自动生成Javabean 在这里需要注意这个接口的Json数据是以中括号{开头是一个Json字符串数组即对应的JavaBean是一个List这里生成的Repo是这个List中元素对应的数据结构。对此我们可以定义获取此数据接口。
public interface NetService { GET (users/{user}/repos) //配置Get请求、URL路径 CallListRepo getRepos(Path(user) String user); //指定返回CallT对象这里的T指定网络解析数据后返回的类型 //Pathuser表示参数user会替换URL路径中的{user}
}3、在AndroidManifest.xml中添加网络权限
uses-permission android:nameandroid.permission.INTERNET/4、实现网络服务接口
创建好接口后接下来就是用Retrofit实现此接口请求逻辑
Retrofit retrofit new Retrofit.Builder() .baseUrl(https://api.github.com) //配置URL的基地址 .addConverterFactory(GsonConverterFactory.*create*()) //配置Gson转换器 .build();
NetService netService retrofit.create(NetService.class); //用Retrofit对象返回一个NetService的实现
CallListRepo octocat netService.getRepos(octocat); //获取Call对象用该对象的enqueue实现异步请求
octocat.enqueue(new CallbackListRepo() { Override public void onResponse(CallListRepo call, ResponseListRepo response) { for (Repo rp : response.body()){ Log.i(TAG,get the id:(rp.getId())); //获取数据后打印ID } } Override public void onFailure(CallListRepo call, Throwable t) { Log.i(TAG,onFailure:(t.toString())); }
});运行后可见打印的日志 至此完整的一次网络请求成功了。当然Retrofit还可以进行更多的复杂操作如配置请求头、请求提、表单提交等更多方式可参考square.github.io/retrofit/。
源码流程图
下面是调用Retrofit做一次网络请求的代码流程 动态代理
Retrofit源码中使用了动态代理在看源码之前我们有必要先了解。在某些场景下我们要用某些功能但不是直接调用实现类而是通过代理类来完成的。通过代理我们可以隐藏实现类的细节在不修改实现类的基础之上增加额外的功能等。在日常生活中代理模式处处可见例如房屋中介、二手车贩子、代购等等。我们所说的代理一般是指静态代理即一个实现类对应一个代理类彼此一一对应。假如你现在是个大型轮胎制造商有家汽车公司准备在你这里订购车轮胎我们用代码实现下
首先实现个接口表示我们要做的事情——造轮胎
public interface Wheel { void produce();
}然后实现这个接口
public class WheelMaker implements Wheel{ Override public void produce() { Log.i(proxyDemo,造好并装上车轮); }
}单独的车轮是没有用的这个时候汽车厂商就来了要把车轮装上去汽车厂商实现代码如下
public class BenZ implements Wheel{ private Wheel wheel; public BenZ(Wheel wheel){ this.wheel wheel; } Override public void produce() { before(); wheel.produce(); after(); } private void after() { Log.i(proxyDemo,BenZ整车组装完毕可以售卖了); } private void before() { Log.i(proxyDemo,BenZ组装好其他零件只缺装轮胎了); }
}汽车厂商来了拿走了轮胎就下来就是组装了组装好就可以售卖然后才有钱回款给你你公司才能赚钱代码如下
Log.*i*(proxyDemo,--------------静态代理-------------);
Wheel wheel new WheelMaker();
BenZ benZ new BenZ(wheel);
benZ.produce();汽车公司拿着你给的轮胎接口后进行组装然后售卖代码很简单没必要过多解释了。对应执行后对应输出如下 随着日积月累你越做越大军方来找你做坦克的车轮。你咬咬牙接了这笔订单。由于军方车轮规格要求更严对应的你重新安排了一条生产线接口做特殊轮胎
public class SpecialWheelMaker implements Wheel{ Override public void produce() { Log.i(proxyDemo,造好特殊车轮并装上-); }
}听说你造好后军方来人拿走你的轮胎回去组装并给了你项目回款
public class Tank implements Wheel{ private SpecialWheelMaker specialWheelMaker; public Tank(SpecialWheelMaker specialWheelMaker){ this.specialWheelMaker specialWheelMaker; } Override public void produce() { before(); specialWheelMaker.produce(); after(); } private void after() { Log.i(proxyDemo,Tank组装完毕准备上战场了); } private void before() { Log.i(proxyDemo,Tank组装好其他零件只缺装轮胎了); }
}组装完毕那天军方邀请你去参观于是你一边吩咐手下人继续做事一边前去参观代码如下
Log.i(proxyDemo,--------------汽车的静态代理-------------);
Wheel wheel new WheelMaker();
BenZ benZ new BenZ(wheel);
benZ.produce();
Log.i(proxyDemo,--------------Tank的静态代理-------------);
SpecialWheelMaker specialWheelMaker new SpecialWheelMaker();
Tank tank new Tank(specialWheelMaker);
tank.produce();对应输出为 你很开心因为随着这些项目的顺利落地你的名声越来越大无数汽车厂商找上门来合作但你也发现成本太高代码臃肿了。什么别摸我、玛傻拉弟弟轮胎都几乎一样除了个别细节不一样外如此为何还要给他们各个品牌之间独立的代理22世纪了早就没有中间商赚差价了于是你自问自答一套工业流程能不能代理所有厂商制造代理呢答案是肯定的。另外从代码的角度上来看如果新增一个厂商就又要新加一个代理类代码量会不停增加而在代理类中的before()和after()都是重复的不能复用。从某种方面来说这就不属于好代码了。
这里就需要用到动态代理了。动态代理不需要事先创建代理汽车厂商类而是根据需求动态创建。相当于一个工厂流水线对应不同种类型轮胎产品不论轮胎类型数量增加多少生产线只有一个。从代码的角度来说不会增加汽车厂商类厂商的公共方法就能得到复用。
首先我们定义一个动态代理类需要实现InvocationHandler接口然后在invoke()中添加相应的逻辑
public class DynamicProxyWheel implements InvocationHandler { private Object wheel; //代理的对象 public DynamicProxyWheel(Object wheel){ this.wheel wheel; } Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); method.invoke(wheel,args); after(); return null; } private void after() { Log.i(proxyDemo,全部组装完毕准备交付); } private void before() { Log.i(proxyDemo,先组装好其他部件及系统); }
}接下来我们使用这个动态代理轮胎来生产各种轮胎了
public class DynamicProxyWheel implements InvocationHandler { private Object wheel; //代理的对象 public DynamicProxyWheel(Object wheel){ this.wheel wheel; } Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); method.invoke(wheel,args); after(); return null; } private void after() { Log.i(proxyDemo,全部组装完毕准备交付); } private void before() { Log.i(proxyDemo,先组装好其他部件及系统); }
}可见这里少了BenZ和Tank这些厂商代理类。这就是动态代理的使用而Retrofit就是通过动态代理的方式创建各种网络接口的代理。
Builder模式
Retrofit retrofit new Retrofit.Builder() .baseUrl(https://api.github.com) //配置URL的基地址 .addConverterFactory(GsonConverterFactory.*create*()) //配置Gson转换器 .build();在声明Retrofit对象的时候我们可以看到这里应用了一个建造者构建者模式建造者模式的特点是可以讲一个复杂对象的构成和表示分离开来。这里我们主要关注build()方法的实现
public Retrofit build() { if (baseUrl null) { throw new IllegalStateException(Base URL required.); } //设置Call的工厂类如果没有设置则为OkHttpClient对象 okhttp3.Call.Factory callFactory this.callFactory; if (callFactory null) { callFactory new OkHttpClient(); } //执行回调方法的对象Android里是在主线程执行回调 Executor callbackExecutor this.callbackExecutor; if (callbackExecutor null) { callbackExecutor platform.defaultCallbackExecutor(); } //网络请求适配器工厂集合 // Make a defensive copy of the adapters and add the default Call adapter. ListCallAdapter.Factory callAdapterFactories new ArrayList(this.callAdapterFactories); callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor)); //数据转换器工厂类集合用于解析网络响应 // Make a defensive copy of the converters. ListConverter.Factory converterFactories new ArrayList( 1 this.converterFactories.size() platform.defaultConverterFactoriesSize()); // Add the built-in converter factory first. This prevents overriding its behavior but also // ensures correct behavior when using converters that consume all types. converterFactories.add(new BuiltInConverters()); converterFactories.addAll(this.converterFactories); converterFactories.addAll(platform.defaultConverterFactories()); return new Retrofit( callFactory, baseUrl, *unmodifiableList*(converterFactories), *unmodifiableList*(callAdapterFactories), callbackExecutor, validateEagerly);
}如果这时候是一头雾水很正常我们先尝试去理解代码到最后我们都会看明白的。 在Build()中初始化了回调Call的工厂类网络请求适配器工厂集合数据转换器工厂集合。可见其中Call的工厂类默认实现为OkHttpClient。默认的CallAdapter.Factory为ExecutorCallAdapterFactory对象。CallAdapter.Factory主要适配接口的返回类型如我们上述例子的接口
public interface NetService { GET (users/{user}/repos) CallListRepo getRepos(Path(user) String user);
}返回为Call类型而Call类型则是由ExecutorCallAdapterFactory适配的如果在创建Retrofit对象的时候指定配置RxJavaCallAdapterFactory
Retrofit retrofit new Retrofit.Builder() .baseUrl(https://api.github.com) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();则接口getRepos的返回类型可为RxJava的Observable。
Build()中还初始化了数据转换器工厂类集合转换器工厂主要负责网络响应的解析比如我们之前代码中是
Retrofit retrofit new Retrofit.Builder() .baseUrl(https://api.github.com) .addConverterFactory(GsonConverterFactory.*create*()) .build();这里设置了GsonConverterFactory我们就可以使用Gson解析网络结果当然还有其他的工厂比如PhotoConverterFactory可以使用PhotoBuf解析网络结果。
build()中值得注意的一行代码是 这里限定了默认的网络回调器通过绑定主线程Looper的Handler强制将网络回调切换到主线程中执行。相比OkHttp3Retrofit在使用时一个很方便地方就是在execute() 或者 enqueue() 发起请求后的返回结果时不需要再切换线程因为此刻它已经在安卓的UI主线程当中了。 这里我们可以总结下这几个成员变量
serviceMethodCache这个map的第一个泛型参数Method即是我们的请求方法。S erviceMethod主要代表网络请求接口中方法进行注解之后我们通过解析解析后拿到的对象与注解中的post、get等方法成对出现一一对应。serviceMethodCache看名字就知道是缓存在这里主要是做网络请求相关配置的缓存之前2.3版本的时候是个LinkedHashMap。
call Factory请求网络的OKHttp的工厂用于“生产”OKHttp的OKHttpClient
baseUrl网络请求的url的基地址与接口参数拼接起来就是个完整的URL
converterFactories数据转换器工厂集合用于生产我们需要的数据转换器数据转换器对我们做了网络请求后得到的response进行转换成我们设定的Java对象
callAdapterFactories网络请求适配器工厂集合用于放置我们的网络请求适配器工厂网络请求适配器把我们的call对象转换成其他类型
callbackExecutor用于执行回调Android类中默认的网络回调执行器为Main ThreadExecutor通过绑定主线程Looper的Handler将网络回调推送到主线程执行。Retrofit当中的网络请求最终都是通过线程池将我们的handler来进行调配可以处理我们的主线程和子线程等线程切换毫无意外我们处理异步的网络请求就需要用到它
validateEagerly一个标记位表示是否需要立即解析我们接口的方法。
我们继续看Builder()在这里Builder是Retrofit的静态内部类 同样也有几个成员变量有两个新参数要了解下
Platform表示Retrofit的适配平台AndroidJava8等
baseUrl网络请求的URL地址注意这里是HttpUrl不是String
converterFactoriescallAdapterFactoriescallbackExecutorvalidateEagerly作用同上。
这里的构造方法中返回的是Platform.get()即适配平台 baseUrl()
Retrofit retrofit new Retrofit.Builder() .baseUrl(https://api.github.com) .addConverterFactory(GsonConverterFactory.*create*()) .build();在原例子中第二行就指定baseUrl()其方法内部主要工作就是对传入的String进行判空和URL转换 注意这里return的是转换好后的HttpUrl类型。 这里的baseUrl()将原String拆分成多个字符碎片然后检测最后一个字符是否是以“/”结尾如果不是则抛出异常baseUrl不是以“/”结尾。如果是的话才return。
addConverterFactory
addConverterFactory()操作比较简单添加工厂集合 那么重点就是里面的参数了 可见最后传的还是Gson转换器工厂对象。
addCallAdapterFactory
Retrofit retrofit new Retrofit.Builder() .baseUrl(https://api.github.com) .addConverterFactory(GsonConverterFactory.*create*()) .addCallAdapterFactory(RxJavaCallAdapterFactory.*create*()) .build();网络请求适配器工厂所做操作与addConverterFactory()一样也都是一样的添加操作。 我们看RxJavaCallAdapterFactory.create()的实现 这个Scheduler就是RxJava中的调度器。也就是说这里的.create()返回了含有Scheduler调度器对象的RxJavaCallAdapterFactory然后添加到callAdapterFactories集合中。
对象构成
这时候再返回我们最初的build() 相信到这里你已经不再是一头雾水了。一句话概括这段代码的含义将retrofit类中的所有成员变量配置完毕完成整个Retrofit对象的构建。
网络请求
在构建好Retrofit对象后我们就要对其进行网络请求了 在这里create()用Retrofit对象返回一个NetService的实现我们观察下其内部实现 validateServiceInterface()方法名字直译就是否是有效服务接口方法内部都是对其是否合法接口的一个判断validateEagerly一个标记位表示是否需要立即解析我们接口的方法。如果是就立即解析。这里我们重点看platform.isDefaultMethod() 判断有没有Java8的TYPES且是默认方法。即判断是不是Java8的默认方法。 这个方法即判断是否是静态方法。
Java接口默认不许有默认实现但是Java8开始可以给接口的一些方法写默认实现了Java接口不允许写静态方法但Java8开始允许接口里写静态方法了。然而Retrofit是不支持的这些的即Retrofit不接纳这两种方式为service接口里的方法。
所以这里判断就是要求不是JAVA8的默认方法和静态方法。这时候才到重点的方法来 也就是说validateServiceInterface()做了一系列接口的合规性验证后最终执行loadServiceMethod()。
在进这个方法之前我们先跳回原外界方法 下面的代码可以看出来是一个动态代理。我们观察其主要逻辑第一个判断方法对象是否是Object对象如果是则直接调用不代理。接着来到return后的判断方法
如果platform.isDefaultMethod(method)则返回platform.invokeDefaultMethod(method, service, proxy, args)如果不是则loadServiceMethod(method).invoke(args)。
为了便于理解上面的三元判断方法等价于
if(platform.isDefaultMethod(method)){ platform.invokeDefaultMethod(method, service, proxy, args)
}else{ loadServiceMethod(method).invoke(args);
}还是判断是否是默认方法如果不是才执行loadServiceMethod(method)。结合上面代码可看出无论validateServiceInterface()最后都会执行loadServiceMethod()可见loadServiceMethod()才是关键中的关键。
loadServiceMethod() 这里的synchronized线程同步锁保证我们的线程安全。
serviceMethod在这里对应的是接口方法的封装。serviceMethodCache在这里是网络请求相关配置的缓存本质是一个Map在这里这个Map的key是method如果能将对应method的value即serviceMethod取出且不为空则return出去。如果为空核心代码又变成了 在这里又去尝试获取serviceMethodCache中的serviceMethod。如果没有得到即为空的情况下执行ServiceMethod.parseAnnotations() 先看① 可以看到这个建造者模式创建了一个完整的method对象包含了网络请求的所有参数methodbaseUrlhttpMethodheaderscontentTypehasBodyisFormEncodedisMultipartisKotlinSuspendFunction等这里不细讲了。
再看② 代码有点多…精简下 static ResponseT, ReturnT HttpServiceMethodResponseT, ReturnT parseAnnotations(Retrofit retrofit, Method method, RequestFactory requestFactory) {boolean isKotlinSuspendFunction requestFactory.isKotlinSuspendFunction;//是否是Kotlin suspend方法...Annotation[] annotations method.getAnnotations();//获取method的注解信息...if (!isKotlinSuspendFunction) {return new CallAdapted(requestFactory, callFactory, responseConverter, callAdapter);} else if (continuationWantsResponse) {//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.return (HttpServiceMethodResponseT, ReturnT)new SuspendForResponse(requestFactory,callFactory,responseConverter,(CallAdapterResponseT, CallResponseT) callAdapter);} else {//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.return (HttpServiceMethodResponseT, ReturnT)new SuspendForBody(requestFactory,callFactory,responseConverter,(CallAdapterResponseT, CallResponseT) callAdapter,continuationBodyNullable);}}这个类中有几个参数要注意一下 callFactory是网络请求工厂用于生产我们的网络请求Call即OKHttp的call。代表着实际的网络请求。
CallAdapte r表示的是网络请求适配器主要的把我们的Call请求适配不同的平台比如RxJava的平台。
responseConverter表示的是数据转换器其实就是reponse内容转换器作用把服务器返回的Json数据转换成我们的JavaBean对象。 这里的httpMethod主要表示网络请求的HTTP方法比如GETPOST等等。 annotations即网络请求方法中的注解即原我们的代码接口中的GET等 parameterTypes即获取我们网络请求接口方法里的类型。 我们回来看其初始化的地方 看到这里遍历工厂集合然后通过get()来获得我们需要的CallAdapter。如果没有合适的就抛出异常。拿到CallAdapter之后拿到其responseType(返回的接口类型)这个时候我们就根据我们网络请求方法的返回值、注解类型从我们的retrofit对象中获取这个网络数据适配器返回的数据类型。 在这里我们通过createResponseConverter获取到数据转换器类型。看下这个方法的实现 这里再通过获取到的注解annotation和之前获取到的responseType再从retrofit中获取 public T ConverterResponseBody, T nextResponseBodyConverter(Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {Objects.requireNonNull(type, type null);Objects.requireNonNull(annotations, annotations null);int start converterFactories.indexOf(skipPast) 1;for (int i start, count converterFactories.size(); i count; i) {ConverterResponseBody, ? converter converterFactories.get(i).responseBodyConverter(type, annotations, this);if (converter ! null) {//noinspection uncheckedreturn (ConverterResponseBody, T) converter;}}StringBuilder builder new StringBuilder(Could not locate ResponseBody converter for ).append(type).append(.\n);if (skipPast ! null) {builder.append( Skipped:);for (int i 0; i start; i) {builder.append(\n * ).append(converterFactories.get(i).getClass().getName());}builder.append(\n);}builder.append( Tried:);for (int i start, count converterFactories.size(); i count; i) {builder.append(\n * ).append(converterFactories.get(i).getClass().getName());}throw new IllegalArgumentException(builder.toString());
}源码逻辑流程很像之前的CallAdapter这里也是遍历converterFactories并从中获取合适的数据转换器工厂然后再通过responseBodyConverter()获取到相应的数据转换器。默认是Json转换器所以这里一般获取到的是JsonResponseConverter了解即可这样就完成了整个数据转换器的初始化工作。 从接下来的代码中可以看出if (!isKotlinSuspendFunction)时即在Java下开发的非协程挂起函数直接返回其子类CallAdapted对象。 这里先暂停我们先看其invoke()HttpServiceMethod extends ServiceMethod而ServiceMethod中有抽象方法invoke() 这个adapt是抽象方法那具体的实现呢就在上面CallAdapted中extends HttpServiceMethod要复写其抽象方法adapt()其核心代码就一行 在说adapt()之前先说说OkHttpCall
OkHttpCall
这个OkHttpCall其实就是对OkHttp中的Call的一个封装。 同时其构造方法也把callFactory、responseConverter等参数传入。 其自身也封装了异步、执行等方法所以retrofit的网络请求到底还是调用的OKHttp库。
adapt 我们上面说到把创建好的OkHttpCall对象传进了callAdapter的adapt()之中并返回。 CallAdapter是个接口本身没有方法实现逻辑。由于这里是动态代理因此我们要到各实现类中去寻找此方法具体实现例如RxJavaCallAdapter等 其主要作用就是把我们的一个一个的Retrofit当中的Call转换成其他平台也可以使用的类型。比如在RxJavaCallAdapter中就转换成了Observable具体转换方式这里就不细说了。
总结下先回到我们的网络请求代码 这里的NetService是个接口接口肯定不能直接调用方法所以是在create()中通过动态代理Proxy.newProxyInstance去进行拦截然后调用其InvocationHandler中的invoke()来进行实际的操作然后通过返回的OkHttpCall对象来进行实际的网络请求。
所以这个netService实际上就是通过动态代理返还过来的OkHttpCall对象。而OkHttpCall对象又是对OkHttp的封装所以这里的.getRepos(“octocat)其实就是通过我们的OkHttp库去请求我们网络然后实行同步和异步的方法。
Retrofit的请求OKHttp的创建
Retrofit的请求其实也分为两种
1、同步OkHttpCall.execute()
2、异步OkHttpCall.enqueue()
这里的OkHttpCall是指的create()里返回的OkHttpCall对象
这里我们先关注我们代码中的异步方法 可见这个call是个接口其所对应的实现类是OkHttpCall。 在这里获取call的方式要关注这个方法 可以看到call通过callFactory创建在原OKHTTP代码中如果要创建一个call则代码是这样的
okHttpClient.newCall(request).enqueue(new Callback());newCall()中参数是一个OkHttp里的Request对象Request对象在Retrofit中通过requestFactory.create()创建创建好后传给callFactory的newCall()并生成okhttp3.call对象。callFactory对象的创建在HttpServiceMethod的parseAnnotations()方法中上面已经说明这里不再赘述。 其对应回调也在OkHttpCall中这时不知道为什么感觉有所遗漏我们找到HttpServiceMethod的parseAnnotations()中的callFactory 原来很早之前在build()里就说明了Retrofit用来创建OkHttp3的Call的工厂就是OkHttp3的OkHttpClient()。同理execute()的创建也是类似这里就不赘述了。此流程一走下来OkHttp对象创建完毕。
数据解析与回调 在call的异步方法里在得到初始的rawResponse后有一个parseResponse()的操作
ResponseT parseResponse(okhttp3.Response rawResponse) throws IOException {ResponseBody rawBody rawResponse.body();// Remove the bodys source (the only stateful object) so we can pass the response along.rawResponse rawResponse.newBuilder().body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())).build();int code rawResponse.code();if (code 200 || code 300) {try {// Buffer the entire body to avoid future I/O.ResponseBody bufferedBody Utils.buffer(rawBody);return Response.error(bufferedBody, rawResponse);} finally {rawBody.close();}}if (code 204 || code 205) {rawBody.close();return Response.success(null, rawResponse);}ExceptionCatchingResponseBody catchingBody new ExceptionCatchingResponseBody(rawBody);try {T body responseConverter.convert(catchingBody);return Response.success(body, rawResponse);} catch (RuntimeException e) {// If the underlying source threw an exception, propagate that rather than indicating it was// a runtime exception.catchingBody.throwIfCaught();throw e;}
}不难看出在这里retorfit将OkHttp返回的数据进行解析然后将返回数据转换后再return重点在这里 这个responseConverter转换器的功能就是将OKHttp返回的不易看懂的数据转换为我们自定义的JavaBean在之前HttpServiceMethod.parseAnnotations()中 前文有讲述最终调用的地方是 遍历converterFactories并从中获取合适的数据转换器工厂然后再通过responseBodyConverter()获取到相应的数据转换器。默认是Json转换器所以这里一般获取到的是JsonResponseConverter了解即可我们可以看出responseConverter来自于converterFactories而在Retrofit的build()中: 而这个this.converterFactories赋值的地方在 即我们写的代码中的 也就是说这个最早初始化的Gson转化器最终被OkHttpCall的parseResponse()中的responseConverter.convert(catchingBody);调用。 这里解析完毕后会执行onResponse回调将OkHttpCall对象和解析数据后的response对象回调出去对应的我们的代码中的 设计模式
经过上述的源码流程梳理不难发现Retrofit其实本质上就是一个网络请求框架的封装。实际上的网络请求等功能实现都是交给了OkHttp去完成。在这些代码封装之中融合了太多的设计模式在里面。
建造者模式 这里很明显有个构建者模式构建者模式将一个复杂对象的构造与它的表示分离使得建造过程可以创建不同的表示 。 其优点很明显这里构建者模式加上链式调用为Retrofit的参数配置增加不少灵活度进一步增强代码可读性。
外观模式 门面模式
门面模式要求一个子系统的外部与其内部通信必须通过一个统一的对象进行。Retrofit给我们暴露的方法和类不多。核心类就是Retrofit我们只管配置Retrofit然后获取接口对象请求数据设置回调其他的都是Retrofit框架内部的事情了。这里Retrofit的门面是Retrofit.create() 。这样的代码设计能降低系统耦合度除了Retrofit平时开发常用的开源框架Glide也有一个门面比如Glide.with(xxx)… 。
动态代理
动态代理指的是程序运行的时候创建代理类的方式代理模式中最重要的就是区分角色 1、目标接口2、目标对象3、代理对象。
Retrofit中则是通过Proxy.newProxyInstance去调用其InvocationHandler中的invoke()来实现动态代理详情上面已经说明这里不再赘述。 装饰模式
装饰模式是在不改变原类文件和使用继承的情况下动态的扩展一个对象的功能。Retrofit中用装饰模式的就是ExecutorCallbackCall了。 简单说下enqueue()方法是异步的当你调用OkHttpCall的enqueue方法回调的callback在子线程中如果需要在主线程接受回调那就要通过Handler转换到主线程上去。ExecutorCallbackCall就是用来干这个事。当然以上是retrofit默认使用的切换线程方式。如果我们指定用rxjava那就不会用到这个ExecutorCallbackCall而是RxJava的Call了。
也许你会感觉装饰模式与静态代理模式很像。但这俩者区别也不小装饰模式里装饰后的对象还是“我”只不过装饰完后“我”的功能更加强大代理模式里对象已经不是“我”了只不过代理模式可以联系到我而已。
策略模式
策略模式定义了一组算法将每个算法都封装起来并且使它们之间可以互换。在CallAdapter中可以添加多个CallAdapter.Factory对象相当于我们封装了多个不同的适配算法CallAdapter.adapt()。上文已经说明其对象的生成初始化和调用都在HttpServerMethod.parseAnnotations()中
static ResponseT, ReturnT HttpServiceMethodResponseT, ReturnT parseAnnotations(Retrofit retrofit, Method method, RequestFactory requestFactory) {......CallAdapterResponseT, ReturnT callAdapter createCallAdapter(retrofit, method, adapterType, annotations);Type responseType callAdapter.responseType();......}......}private static ResponseT, ReturnT CallAdapterResponseT, ReturnT createCallAdapter(Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {try {//noinspection uncheckedreturn (CallAdapterResponseT, ReturnT) retrofit.callAdapter(returnType, annotations);} catch (RuntimeException e) { .......}}然后方法执行到Retrofit对象的callAdapter() - nextCallAdapter()。 在的nextCallAdapter()中根据返回值retrunType类型遍历调用CallAdapter.Factory的get()方法 如果返回的CallAdapter对象不为null则直接返回该对象此逻辑即印证了CallAdapter.Factory的get()应该是根据retrunType确定是否返回CallAdapter对象是否选择该种策略。
适配器模式
适配器模式将一个类的接口变换为客户端期待的另一种接口从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。Retrofit代码中将大量的设计模式融合在一起除了策略模式CallAdapter中还有此模式
这段注释似乎就是在说明将响应类型为{code R}的{link Call}修改为{code T}类型。Retrofit中CallAdapter就采用了适配器模式为创建访问Call接口提供服务。默认情况下使用默认的 ExecutorCallAdapterFactory 将okhttp3.call转变成为 retroift中的call如果设定RxJava则将okhttp3.call转化为 Observable。上述源码有分析此处不赘述。 如果你对上述中所描述的知识点还不是很清楚的话推荐你看下 《OKhttp 源码解析》里面记录的知识点比较详细有需要的可以 点击这里直接获取里面记录许多Android 相关学习知识点。↓↓↓ Android 技术提升知识点归整
Android 性能调优系列https://qr18.cn/FVlo89 Android 车载学习指南https://qr18.cn/F05ZCM Android Framework核心知识点笔记https://qr18.cn/AQpN4J Android 音视频学习笔记https://qr18.cn/Ei3VPD Jetpack全家桶含Composehttps://qr18.cn/A0gajp Kotlin 入门到精进https://qr18.cn/CdjtAF Flutter 基础到进阶实战https://qr18.cn/DIvKma Android 八大知识体系https://qr18.cn/CyxarU Android 中高级面试题锦https://qr18.cn/CKV8OZ
后续如有新知识点将会持续更新尽请期待……