Retrofit流程及设计模式全解析
版权声明:本文为openXu原创文章【openXu的博客】,未经博主允许不得以任何形式转载
文章目录
- 1. 代理模式
- 1.1 静态代理模式
- 1.2 动态代理模式
- 1.3 动态代理的原理
- 2. Retrofit接口代理对象的创建
- 3. Retorfit的Call创建
- 4. CallAdapter接口适配器
- 4.1 CallAdapter & Factory
- 4.2 平台默认的DefaultCallAdapterFactory
- 4.3 RxJava3CallAdapterFactory使Retrofit支持RxJava
- 5. Retrofit请求流程
- 5.1 RequestFactory解析接口方法配置创建Request请求对象
- 5.2 okhttp3.Call的创建
- 5.3 Converter请求和响应数据转换
- requestBodyConverter()&&stringConverter()转换接口定义的参数
- responseBodyConverter()转换okhttp响应数据
- 6. Retrofit对协程的支持
- 6.1 suspend接口的执行
- 7 Retrofit调用流程
- 8 Retrofit的设计模式分析
- 8.1 外观模式(Facade Pattern)
- 8.2 建造者模式(Builder Pattern)
- 8.3 动态代理模式(Proxy Pattern)
- 8.4 静态代理模式(Proxy Pattern)
- 8.5 装饰器模式(Decorator Pattern)
- 8.6 工厂模式(Factory Pattern)
- 简单工厂模式
- 工厂模式
- 抽象工厂模式
- 8.7 适配器模式(Adapter Pattern)
- 8.8 策略模式(Strategy Pattern)
- 8.9 观察者模式(Observer Pattern)
- 8.10 原型模式(Prototype Pattern)
- 8.11 享元模式(Flyweight Pattern)
- 8.12 单例模式(Singleton Pattern)
本文基于Retrofit 2.9.0版本源码分析,根据Retrofit源码窥探请求流程及框架设计中使用到的设计模式
1. 代理模式
在文章开始之前先介绍一下代理模式,因为这是Retrofit的入口,其他设计模式参考文章末尾的概括
代理模式:为对象提供一种代理以控制这个对象的访问。某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
组成:
- 抽象角色:通过接口或抽象类声明真实角色实现的业务方法
- 真实角色(委托类):实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
- 代理角色(代理类):实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
//抽象
interface IFlyable{void fly() throws Exception;
}
//真实类
class Bird implements IFlyable {@Overridepublic void fly() throws Exception {Thread.sleep(1000);System.out.println("鸟挥动翅膀起飞了...");}
}
//代理类
class FlyTimeProxy implements IFlyable {private IFlyable flyable;public FlyTimeProxy(IFlyable flyable) {this.flyable = flyable;}@Overridepublic void fly() throws Exception {long start = System.currentTimeMillis();flyable.fly(); //代理类调用真实类long end = System.currentTimeMillis();System.out.println("方法执行时长:" + (end - start));}
}class Test {public static void main(String[] args) throws Exception {//通过代理类间接访问委托类new FlyTimeProxy(new Bird()).fly();}
}
代理模式分为静态代理和动态代理
1.1 静态代理模式
上面示例代码中,通过FlyTimeProxy
代理来控制IFlyable
的目标子类对象,为fly方法增加了时间统计。在编写代码的时候代理类方法中直接调用了委托类,这就属于静态代理。静态代理是由程序员创建或工具生成代理类,是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
缺点:
- 代理类和委托类实现了相同的接口,出现了大量的代码重复。
- 代理对象只能服务于特定的委托对象,如果需要代理其他类,则要为其他类创建代理类。
比如FlyTimeProxy
现在只能代理IFlyable
的子类对象为fly方法计算执行时间,现在我需要为IRunnable
会跑的run()
也计算时间,就只能创建一个RunTimeProxy
代理对象了。
有没有办法我只创建一个TimeProxy
就可以代理所有类,为其方法计算执行时间呢?
1.2 动态代理模式
动态代理是在实现阶段不用关心代理类,而在运行阶段才动态创建出代理对象,它是基于反射实现的。Spring的两大思想IoC(依赖注入控制反转)和AOP(面向切面编程:在不改变源码的情况下,在方法中插入自定义逻辑)中的AOP就是基于动态代理机制实现的。
JDK提供了java.lang.reflect.InvocationHandler
和java.lang.reflect.Proxy
类来实现动态代理机制,Proxy是自动生成代理类的父类,它维护了InvocationHandler的实例对象,并提供了根据委托类自动创建代理对象的方法Proxy.newProxyInstance()
。当调用代理对象(Proxy的子类对象)的方法时实际上是调用InvocationHandler.invoke()
方法,从而控制委托类的调用
//抽象
interface IFlyable{void fly() throws Exception;
}
//真实类
class Bird implements IFlyable {@Overridepublic void fly() throws Exception {Thread.sleep(1000);System.out.println("鸟挥动翅膀起飞了...");}
}
/**InvocationHandler:提供了代理对象调用委托对象的功能*/
class TimeHandler implements InvocationHandler{private Object target; // 目标对象public Object createProxyInstance(Object target){this.target=target;/*** Proxy:提供用于创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。* 第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器* 第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口* 第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法*/return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}/*** 当调用代理类方法时,将会执行此方法* @param proxy 代理对象* @param method 目标对象的方法* @param args 方法参数*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//在调用目标类方法前后插入逻辑long start = System.currentTimeMillis();method.invoke(target, args); //调用真实类long end = System.currentTimeMillis();System.out.println("方法执行时长:" + (end - start));return null;}
}class Test {public static void main(String[] args) throws Exception {//通过代理类间接访问委托类IFlyable proxy = (IFlyable) new TimeHandler().createProxyInstance(new Bird());proxy.fly();}
}
1.3 动态代理的原理
动态代理的原理是为委托类自动创建代理类,并通过反射获取代理类的实例。当调用代理类对象方法时,实际上时调用了InvocationHandler.invoke()
从而实现拦截。如下自动生成的代理类简约代码:
public final class $Proxy0 extends Proxy implements IFlyable{//代理类Proxy维护了InvocationHandler实例public $Proxy0(InvocationHandler invocationhandler){super(invocationhandler);}public final void fly(String s, String s1){//调用代理类的方法实际上是调用InvocationHandler.invoke()从而实现拦截super.h.invoke(this, m3, new Object[] {s, s1}); }private static Method m3;static {m3 = Class.forName("IFlyable").getMethod("fly", new Class[] {});}
}
2. Retrofit接口代理对象的创建
通过retrofit.create(ApiService::class.java)
获取接口的实例对象,这个对象是一个动态代理对象,当调用代理对象的方法时,会被拦截得到方法、参数,再调用loadServiceMethod()
解析接口上的注解,封装成一个HttpServiceMethod
对象并调用其invoke()
执行Okhttp请求
/**Retrofit.java*/
public <T> T create(final Class<T> service) {//检查接口类型是否符合要求:必须是接口、不是泛型接口validateServiceInterface(service);//★★★代理模式获取接口的代理对象return (T)Proxy.newProxyInstance(service.getClassLoader(),new Class<?>[] {service},new InvocationHandler() {private final Platform platform = Platform.get();private final Object[] emptyArgs = new Object[0];//调用接口代理对象方法实际上是调用了InvocationHandler.invoke()@Overridepublic @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)throws Throwable {// 如果方法是接口继承自Object的方法,则遵循常规调用if (method.getDeclaringClass() == Object.class) {return method.invoke(this, args);}args = args != null ? args : emptyArgs;//如果是默认方法(如java8)就执行 platform 的默认方法return platform.isDefaultMethod(method)? platform.invokeDefaultMethod(method, service, proxy, args)/*** ★★★调用HttpServiceMethod.invoke()为Retrofit接口返回具体的请求类型对象,比如接口中定义的Call、Observable等* 该语句主要做了2件事:* ①. Retrofit的Call创建* ②. 通过接口适配器CallAdapterFactory将Call对象转换成其他对象(RxJava方式、挂起函数等)*/: loadServiceMethod(method).invoke(args);}});
}
3. Retorfit的Call创建
通过Retrofit注解定义的服务器接口方法类我们称为接口,看源码之前,我们要弄清涉及到的主要类以及他们的作用:
ServiceMethod
:这个抽象类代表一个接口方法对象,它主要提供了通过RequestFactory
解析方法上注解的功能,然后交给它的子类HttpServiceMethod
处理,并定义了invoke方法,用于返回接口方法的值RequestFactory
:这个类用于解析接口方法的注解,并提供了create(Object[] args)
方法根据注解配置创建出okhttp3.Request
对象,Request代表Okhttp的一个请求HttpServiceMethod
:它是ServiceMethod的抽象子类,它将接口方法调用适配为一个Http调用。它维护了requestFactory
,具备将接口方法转换为Request对象的能力,并且实现了invoke方法用于创建了Retrofit的Call对象,并通过接口适配器将Call转换成接口方法对应的返回类型
/**Retrofit.java*/
//接口可能被请求多次,缓存提升性能
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
ServiceMethod<?> loadServiceMethod(Method method) {//从缓存中获取接口方法对应的ServiceMethod对象,ServiceMethod<?> result = serviceMethodCache.get(method);if (result != null) return result;synchronized (serviceMethodCache) {result = serviceMethodCache.get(method);if (result == null) {//1 解析接口方法上的注解,创建ServiceMethod对象result = ServiceMethod.parseAnnotations(this, method);serviceMethodCache.put(method, result); //缓存}}return result;
}/**ServiceMethod.java*/
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {//2 通过RequestFactory解析接口上的注解RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);...//3 获取一个HttpServiceMethod的子类对象return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}/**HttpServiceMethod将接口方法的调用适配为一个Http调用*/
abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {//根据接口方法配置创建一个HttpServiceMethod子类对象static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(Retrofit retrofit, Method method, RequestFactory requestFactory) {...//接口适配器CallAdapter<ResponseT, ReturnT> callAdapter =createCallAdapter(retrofit, method, adapterType, annotations);//4 返回一个HttpServiceMethod的子类对象,并将接口适配器传入构造方法if (!isKotlinSuspendFunction) {return new CallAdapted<>(..., callAdapter);} else if (continuationWantsResponse) {return (HttpServiceMethod<ResponseT, ReturnT>)new SuspendForResponse<>(...,(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);}...}@Overridefinal ReturnT invoke(Object[] args) {//5 ★★★创建一个Retrofit的Call对象,表示一个网络请求任务Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);//6 ★★★adapt的目的是将Call转换成其他对象类型,比如RxJava的Observablereturn adapt(call, args);}
}
当调用接口代理对象方法时,实际上是调用到了loadServiceMethod(method).invoke(args)
,loadServiceMethod(method)
的作用是根据接口方法的定义创建出不同的HttpServiceMethod
子类对象,然后调用该对象的invoke()方法,invoke中创建了Call对象(OkHttpCall
的实例)。
然后通过adapt()
方法将Call对象转换成接口真正的返回类型。比如下面定义的两个接口,调用login()
时,adapt将返回Call对象,调用login1()
时它将返回一个Observable
对象,adapt()
是通过调用接口适配器对象CallAdapter.adapt(call)
来对Call对象进行转换的。
ApiService接口|--fun login():Call<User> //Call|--fun login1(): Observable<User> //RxJava
4. CallAdapter接口适配器
在定义Retrofit服务器接口时,可以支持返回Call、Observable等等,如下:
ApiService接口|--fun login():Call<User> //Call|--suspend fun login1(): User //suspend协程挂起函数,稍后单独讲|--fun login2(): Observable<User> //RxJavaprivate val retrofit = Retrofit.Builder()....addCallAdapterFactory(RxJava3CallAdapterFactory.create()) //添加对RxJava的支持.build()
Retrofit可以根据接口方法返回类型自动适配返回对应的对象,这都是接口适配器的作用。接下来我们看看接口适配器有哪些?他们是怎么工作的?
4.1 CallAdapter & Factory
/**将R适配为T*/
public interface CallAdapter<R, T> {//Retrofit接口方法返回类型的泛型参数类型,比如方法返回类型<User>,此处将返回UserType responseType();//将R转换为T返回T adapt(<R> );abstract class Factory {/**根据Retrofit接口方法的返回类型创建一个适配器实例对象 */public abstract @Nullable Adapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit);...}
}
CallAdapter
接口代表接口适配器,它有一个内部抽象工厂类Factory
。适配器工厂CallAdapter.Factory的作用是提供了get()
方法判断匹配创建适配器CallAdapter对象,而适配器提供了adapt()
将OkHttpCall
类型的call对象转换成接口方法返回值对应类型对象。
构建Retrofit时可添加很多种接口适配器工厂以适配接口方法的不同返回类型,这其中就包括平台默认的接口适配器和通过addAdapterFactory()
额外添加的,这些适配器工厂都被保存在callAdapterFactories
集合中,这就存在两个问题:
- 怎样从集合中根据接口方法匹配到合适的适配器对象?
- 适配器是怎么完成call转换的?
public final class Retrofit {//接口适配器工厂集合,构造方法中被赋值final List<CallAdapter.Factory> callAdapterFactories; //Retrofit建造者public static final class Builder {private final Platform platform; //代表当前平台,比如AndroidBuilder(Platform platform) {this.platform = platform;}//添加额外的接口适配器工厂支持public Builder addAdapterFactory(Adapter.Factory factory) { AdapterFactories.add(Objects.requireNonNull(factory, "factory == null"));return this;}public Builder() { this(Platform.get());}public Retrofit build() {...//★★★接口适配器工厂集合,首先包含了通过addCallAdapterFactory添加的接口适配器工厂List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);//★★★添加平台默认的接口适配器,platform代表平台,比如Android平台默认的接口适配器是DefaultCallAdapterFactory,如果是使用Java8则还会有CompletableFutureCallAdapterFactory(这个适配器将支持kotlin挂起函数)callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));...//创建Retrofit对象,并将接口适配器通过构造方法传入return new Retrofit(...,unmodifiableList(callAdapterFactories),...);}}/**★★★根据接口返回类型获取与之匹配的适配器对象*/public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {int start = callAdapterFactories.indexOf(skipPast) + 1;for (int i = start, count = callAdapterFactories.size(); i < count; i++) {//遍历适配器工厂集合CallAdapter<?, ?> adapter = callAdapterFactories.get(i) //根据接口返回类型returnType判断是否符合要求,如果不符合返回null,符合则创建一个接口适配器对象.get(returnType, annotations, this); if (adapter != null) //直到匹配到合适的接口适配器,并返回return adapter;}...}...
}
HttpServiceMethod的invoke()方法首先创建了一个OkHttpCall
类型的call对象,然后调用adapt(call, args)
将call转换成接口返回类型。adapt()
方法实际上是通过调用HttpServiceMethod
的子类维护的callAdapter
接口适配器对象的callAdapter.adapt(call)
方法完成的。
HttpServiceMethod.parseAnnotations()
方法中通过createCallAdapter()
为接口方法创建了一个接口适配器CallAdapter对象并传给其子类对象,跟踪代码发现最后调用到Retrofit的nextCallAdapter()
方法去获取适配器对象。该方法将遍历callAdapterFactories
集合,调用CallAdapter.Factory.get()
方法来获取适配器对象,如果接口返回类型returnType和适配器正好匹配上则创建一个适配器对象,否则返回空继续遍历直到匹配到合适的接口适配器,这就回答了上述第一个问题。
关于call是怎么转换的,接下来我们通过查看几个具体的接口适配器就明白了。
4.2 平台默认的DefaultCallAdapterFactory
Retrofit默认提供对Call的支持,因为Retrofit.build()
时添加了Android
平台默认的接口适配工厂DefaultCallAdapterFactory
,该工厂会判断接口方法的返回类型是否是Call类型,如果是则创建默认接口适配器。
该适配器的adapt()
将原始OkHttpCall
转换成ExecutorCallbackCall
类型,他们都是Call的子类,ExecutorCallbackCall
的目的是将Call异步请求的结果切回主线程中。
接口适配器将OkHttpCall
转换其他返回类型,其他类型像是原始OkHttpCall
的静态代理类,又可以看作是它的包装类。所以这里既可以理解为静态代理模式,也可以说是装饰者模式,代理模式和装饰者模式有一个共同点就是调用目标对象实现功能,但是他们的出发点是不一样的,代理模式是用来控制目标对象的调用,而装饰者模式则是对目标对象功能增强
/**Android平台默认的接口适配器工厂,用于支持接口方法返回Call类型*/
final class DefaultCallAdapterFactory extends CallAdapter.Factory {private final Executor callbackExecutor;@Overridepublic @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {//★★★1. 如果接口返回类型不是Call,则返回nullif (getRawType(returnType) != Call.class)return null;...//否则创建一个默认适配器对象return new CallAdapter<Object, Call<?>>() {...@Overridepublic Call<Object> adapt(Call<Object> call) {//★★★2. 将原来的OkHttpCall对象转换成ExecutorCallbackCall对象并返回return executor == null ? call : new ExecutorCallbackCall<>(executor, call);}};}/**这才是Retrofit接口方法返回Call类型时的真实类*/static final class ExecutorCallbackCall<T> implements Call<T> {final Executor callbackExecutor;final Call<T> delegate;//重写了enqueue异步请求方法,目的是通过callbackExecutor将请求结果切换到主线程@Overridepublic void enqueue(final Callback<T> callback) {Objects.requireNonNull(callback, "callback == null");//调用目标对象(OkHttpCall)delegate.enqueue(new Callback<T>() {@Overridepublic void onResponse(Call<T> call, final Response<T> response) {//★★★3.请求结果切回主线程callbackExecutor.execute(() -> {...callback.onResponse(ExecutorCallbackCall.this, response);});}@Overridepublic void onFailure(Call<T> call, final Throwable t) {callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));}});}...}
}
/**Android平台*/
static final class Android extends Platform {@Overridepublic Executor defaultCallbackExecutor() {return new MainThreadExecutor();}//Android平台提供,用于将请求结果回调切回主线程中static final class MainThreadExecutor implements Executor {private final Handler handler = new Handler(Looper.getMainLooper());@Overridepublic void execute(Runnable r) {handler.post(r);}}
}
4.3 RxJava3CallAdapterFactory使Retrofit支持RxJava
上面查看了默认的接口适配器工作原理,相信对RxJava的支持就不用多说了吧?主要看关键的两点:
- RxJava3CallAdapterFactory的
get
方法判断接口方法返回类型是否为RxJava的,是则创建适配器对象 - RxJava3CallAdapter的
adapt
方法将原始OkHttpCall
对象包装为Observable
的子类对象
/**适配器工厂,继承自CallAdapter.Factory*/
public final class RxJava3CallAdapterFactory extends CallAdapter.Factory {public static RxJava3CallAdapterFactory create() {return new RxJava3CallAdapterFactory(null, true);}...@Overridepublic @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {//判断接口返回类型是否是RxJava对应的一些类型Class<?> rawType = getRawType(returnType);boolean isFlowable = rawType == Flowable.class;boolean isSingle = rawType == Single.class;boolean isMaybe = rawType == Maybe.class;//如果不是,则返回nullif (rawType != Observable.class && !isFlowable && !isSingle && !isMaybe)return null;...//否则创建一个RxJava3接口适配器对象return new RxJava3CallAdapter(...);}
}
/**RxJava3接口适配器,实现CallAdapter接口*/
final class RxJava3CallAdapter<R> implements CallAdapter<R, Object> {...//将Call转换为RxJava对应的观察者Observable对象并返回@Overridepublic Object adapt(Call<R> call) {Observable<Response<R>> responseObservable =isAsync ? new CallEnqueueObservable<>(call) : new CallExecuteObservable<>(call);Observable<?> observable;if (isResult) {observable = new ResultObservable<>(responseObservable);} else if (isBody) {observable = new BodyObservable<>(responseObservable);} else {observable = responseObservable;}...if (isFlowable) return observable.toFlowable(BackpressureStrategy.LATEST);if (isSingle)return observable.singleOrError();if (isCompletable)return observable.ignoreElements();return RxJavaPlugins.onAssembly(observable);}
}
5. Retrofit请求流程
/**OkHttpCall.java*///异步请求
public void enqueue(final Callback<T> callback) {//1. 声明一个okhttp3.Call对象,用来进行网络请求okhttp3.Call call;Throwable failure;synchronized (this) {call = rawCall;if (call == null && failure == null)//★ 2. 创建okhttp3.Call对象call = rawCall = createRawCall();}//★ 3. 通过okhttp3.Call发起真正的网络请求call.enqueue(new okhttp3.Callback() {@Overridepublic void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {Response<T> response;//★ 4. 解析请求响应结果response = parseResponse(rawResponse);//5. 请求成功回调callback.onResponse(OkHttpCall.this, response);}private void callFailure(Throwable e) {//6. 请求失败回调callback.onFailure(OkHttpCall.this, e);}});
}//同步请求
public Response<T> execute() throws IOException {okhttp3.Call call;//获取okhttp3.Call对象,其实也是调用createRawCall()call = getRawCall();...// 解析请求响应结果return parseResponse(call.execute());
}
不管接口方法返回的是Call还是Observable还是其他类型,请求都是通过OkHttpCall
的实例对象发起的(其他类型比如被观察者Observable中维护了原始Call对象)。拿到Retrofit的Call
对象后就可以调用call.enqueue(Callback)
发起异步请求了,enqueue方法中创建了okhttp3.Call
对象用于发送真正的网络请求,然后通过回调将请求结果返回。这只是一个大概的请求流程,还有很多细节比如怎样根据接口上的创建Request请求对象?okhttp3.Call是怎样创建的?响应数据怎样解析的?等等
Retrofit是通过OkHttp完成网络请求的,在这个过程中有3个重要的转换:
- Retrofit接口方法 --> OkHttp的Request请求对象
- Retrofit的OkHttpCall对象 -->
okhttp3.Call
对象 - OkHttp请求结果
okhttp3.Response
--> Retrofit的Response
5.1 RequestFactory解析接口方法配置创建Request请求对象
创建okhttp3.Call
对象是需要首先构造一个Request对象,Request表示一个http请求,封装了请求url、参数和其他配置数据。Retrofit通过RequestFactory
类解析接口方法上的注解和参数,然后保存在其成员变量中,在调用OkHttpCall发起请求时,首先会通过requestFactory.create(args)
创建一个Request对象,然后交给callFactory(OkHttpClient)创建okhttp3.Call
对象。在创建Request对象时,涉及到Converter
的使用,用于将接口方法参数上的实参对象转换为okhttp的RequestBody
,这块请看5.3对Converter的讲解
/**ServiceMethod.java*/
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {//1. 通过RequestFactory解析接口注解RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);...return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}/**该类主要工作是解析接口上的注解,并根据注解封装Request对象*/
final class RequestFactory {//★★★解析注解的结果保存在RequestFactory成员变量中final String httpMethod;private final String relativeUrl;private final Headers headers;private final MediaType contentType;private final boolean hasBody;private final boolean isFormEncoded;...static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {//2 构建者模式return new Builder(retrofit, method).build();}static final class Builder {RequestFactory build() {//3 遍历接口方法上的注解并解析for (Annotation annotation : methodAnnotations) { //methodAnnotations = method.getAnnotations()parseMethodAnnotation(annotation);}...// 遍历解析接口方法的参数int parameterCount = parameterAnnotationsArray.length;for (...) {parameterHandlers[p] = parseParameter(...);}return new RequestFactory(this);}//4 解析接口方法上的注解,最后将解析结果保存在RequestFactory对象中private void parseMethodAnnotation(Annotation annotation) {if (annotation instanceof GET) { //解析请求方式parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);} else if (annotation instanceof POST) {parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);}...else if (annotation instanceof retrofit2.http.Headers) { //解析请求头String[] headersToParse = ((retrofit2.http.Headers) annotation).value();headers = parseHeaders(headersToParse);}...}}//★ 5 根据接口上的注解和方法参数构建Request对象okhttp3.Request create(Object[] args) throws IOException {ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;RequestBuilder requestBuilder =new RequestBuilder( httpMethod, baseUrl, relativeUrl, headers,...);...return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();}
}/**retrofit2.OkHttpCall.java*/
private final okhttp3.Call.Factory callFactory;
private okhttp3.Call createRawCall() throws IOException {//通过callFactory(OkHttpClient)创建okhttp3.Call对象okhttp3.Call call = callFactory.newCall(requestFactory.create(args));...return call;
}
5.2 okhttp3.Call的创建
/**retrofit2.OkHttpCall.java*/private final okhttp3.Call.Factory callFactory;//接上面源码注释第2点,通过callFactory创建okhttp3.Call对象private okhttp3.Call createRawCall() throws IOException {okhttp3.Call call = callFactory.newCall(requestFactory.create(args));...return call;}
okhttp3.Call
是通过callFactory
创建的,逆向跟踪发现它是在HttpServiceMethod.parseAnnotations()
方法中通过okhttp3.Call.Factory callFactory = retrofit.callFactory
赋值的,而retrofit.callFactory
不就是我们创建Retrofit对象时设置的OkHttpClient
对象吗?
/**OkhttpClient*/
private val okHttpClient = OkHttpClient.Builder().addNetworkInterceptor(logInterceptor).build()
/**Retrofit*/
private val retrofit = Retrofit.Builder().client(okHttpClient) //设置callFactory.baseUrl(ApiService.BASE_URL).addConverterFactory(MoshiConverterFactory.create(MoshiHelper.moshi)).addCallAdapterFactory(RxJava3CallAdapterFactory.create()).build()public Retrofit build() {okhttp3.Call.Factory callFactory = this.callFactory;if (callFactory == null) {callFactory = new OkHttpClient(); //创建默认的callFactory}
}
所以okhttp3.Call
实际上是通过OkHttpClient.newCall()
创建的:
@Override public Call newCall(Request request) {return RealCall.newRealCall(this, request, false /* for web socket */);
}
5.3 Converter请求和响应数据转换
fun login(@Body body: LoginBody): Call<User>
如果我们定义了上面的接口,当我们调用该接口时会做如下事情:
- 创建一个Retrofit的OkHttpCall对象
- 使用RequestFactory解析接口的注解和参数,并调用
create()
创建一个okhttp的Request请求对象 - 在创建Request对象过程中,需要通过
Converter
将接口参数数据LoginBody转换为okhttp的RequestBody
- 通过Request创建okhttp的
okhttp3.Call
对象,发起请求 - 请求响应后,将okhttp的响应对象中的
ResponseBody
通过Converter
转换为我们需要的User
对象
为什么涉及这么多的转换呢?因为Retrofit和OkHttp原本是不相干的两个框架,Retrofit负责http协议的封装,okhttp负责请求。但是通过Retrofit封装的请求okhttp并不认识,所以需要将请求接口中的数据转换成okhttp认识的对象RequestBody,反过来okhttp响应的数据ResponseBody类型Retrofit也不认识,同样需要将它转换成接口定义中的响应类型User对象。
Converter与上面讲的CallAdapter不同的地方是,CallAdapter只有一个转换方法就是将OkHttpCall转换为接口返回的类型,而Converter有两个转换,请求数据转换和响应数据转换。关于转换器工厂可参照上面讲述的接口适配器工厂,都是工厂模式,区别是转换器的工厂是抽象工厂模式,而接口适配器工厂是工厂模式,关于工厂模式请参考文章最后对设计模式的总结
requestBodyConverter()&&stringConverter()转换接口定义的参数
RequestFactory.create()创建Request请求对象时,会将解析接口的参数(封装在ParameterHandler
数组中,为什么是数组,因为接口参数可能多个,不同参数的注解对应不同的ParameterHandler子类,提供了apply()
方法完成参数的转换,而apply则是调用了Converter.convert()
完成转换的)转换为RequestBody或者String,ParameterHandler中维护了一个Converter
对象,是通过retrofit
对象的retrofit.stringConverter()
和retrofit.requestBodyConverter()
获取的,为什么会有两个方法?因为参数的注解不同,比如@Url注解的参数需要转换为String,而@Body注解的参数需要转换为RequestBody。
Retrofit并没有配置默认的转换器,需要我们通过addConverterFactory()
添加转换器工厂,比如我们添加的addConverterFactory(GsonConverterFactory.create())
看看GsonConverterFactory
工厂生产的转换器是怎么转换的:
/**转换器工厂类,跟CallAdapter.Factory原理差不多*/
public final class GsonConverterFactory extends Converter.Factory {public static GsonConverterFactory create() {return create(new Gson());}private final Gson gson;//获取响应数据转换器@Overridepublic Converter<ResponseBody, ?> responseBodyConverter(...) {TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));return new GsonResponseBodyConverter<>(gson, adapter);}//★获取请求数据转换器@Overridepublic Converter<?, RequestBody> requestBodyConverter(...) {TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));return new GsonRequestBodyConverter<>(gson, adapter);}
}
/**★数据转换器*/
final class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> {...@Override public RequestBody convert(T value) throws IOException {//★将参数数据对象转换为json字符串后,封装到RequestBody中Buffer buffer = new Buffer();Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);JsonWriter jsonWriter = gson.newJsonWriter(writer);adapter.write(jsonWriter, value);jsonWriter.close();return RequestBody.create(MEDIA_TYPE, buffer.readByteString());}
}
发现GsonRequestBodyConverter
实现了convert()
方法中就是将参数数据对象比如LoginBody转换为json字符串后封装到RequestBody
中,然后返回RequestBody
对象,是不是很简单?
为什么GsonConverterFactory没有重写stringConverter()
创建String转换器对象呢?因为Retorfit的stringConverter()
方法针对String的转换默认返回了return (Converter<T, String>) BuiltInConverters.ToStringConverter.INSTANCE
:
static final class ToStringConverter implements Converter<Object, String> {static final ToStringConverter INSTANCE = new ToStringConverter();@Overridepublic String convert(Object value) {return value.toString(); //直接返回object.toString}}
responseBodyConverter()转换okhttp响应数据
OkHttpCall的enqueue()
和execute()
执行请求后都是调用了parseResponse(rawResponse)
解析响应结果,这个方法将ResponseBody
通过数据转换器responseConverter
转换为对应的泛型类型数据,然后再封装到Retrofit的Response对象返回。
/**retrofit2.OkHttpCall.java*/
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {//从okhttp的Response响应结果中获取响应数据体ResponseBody rawBody = rawResponse.body();...ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);try {//★ 将okhttp3.Response的body转换为我们需要的数据类型T body = responseConverter.convert(catchingBody);//构造Retrofit的Response对象并返回return Response.success(body, rawResponse);} catch (RuntimeException e) {}
}
比如fun login():Call<User>
这个接口在请求响应后会将ResponseBody
转换成User
对象,这是通过Converter
完成的,在追踪它的源码时,发现它和CallAdapter
接口适配器的套路都是一样的。
Retrofit维护了一个List<Converter.Factory> converterFactories
转换器工厂集合,并在build()方法中添加了默认的转换器工厂BuiltInConverters
,Retrofit提供了nextResponseBodyConverter()
方法用来根据接口方法的返回泛型类型获取一个转换器对象,该方法遍历工厂集合根据接口方法的泛型类型匹配对应的转换器对象。
比如fun login():Call<ResponseBody>
接口得到的转换器就是系统默认的BuiltInConverters
工厂创建的转换器,这个工厂是专门匹配泛型类型是ResponseBody
的接口的。
如果接口变成fun login():Call<User>
,现在转换器工厂集合中只有默认的BuiltInConverters
,而默认工厂发现接口泛型不是ResponseBody
就不会为它创建转换器对象,这时候程序就会抛异常。如果想要正确的解析User对象,需要我们通过Retrofit.Builder().addConverterFactory(GsonConverterFactory.create())
添加额外的转换器工厂,上面GsonConverterFactory
源码中的responseBodyConverter()
方法返回的转换器对象的作用就是将okhttp返回的ResponseBody转化为接口方法中的具体类型:
final class MoshiResponseBodyConverter<T> implements Converter<ResponseBody, T> {...@Overridepublic T convert(ResponseBody value) throws IOException {BufferedSource source = value.source();...JsonReader reader = JsonReader.of(source);//将ResponseBody通过json反序列化为具体类型T result = adapter.fromJson(reader); return result;}
}
6. Retrofit对协程的支持
ApiService接口|--suspend fun login(): User //★ suspend协程挂起函数|--fun login1():Call<User> //Call|--fun login2(): Observable<User> //RxJava
如果没有用过Retrofit+协程的可以尝试一下,可以说非常香。关于协程相关内容这里就不便展开了,有机会单独介绍,这里主要看看Retrofit是怎么支持挂起函数的。
接口方法解析器RequestFactory
用于解析方法上的注解和参数,怎样判断一个方法是挂起函数呢?判断有没有suspend
修饰?kotlin最终都会被编译为java class,编译之后的class是没有这个修饰符的(java没有这个关键字),所以这种方式是不可行的。
挂起函数在被编译为class字节码后,函数会增加一个类型为Continuation
(续体)的参数,RequestFactory正式通过这一点来确定方法是否为挂起函数的:
可以自己写一个挂起函数,编译成class然后反编译为java文件查看参数是否多了一个
Continuation
。借助Android Studio–>Tools菜单–>Kotlin–>Show Kotlin Bytecode–>Decompile查看kotlin对应的java源码
/**RequestFactory.java*/
private @Nullable ParameterHandler<?> parseParameter(int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {ParameterHandler<?> result = null;...if (Utils.getRawType(parameterType) == Continuation.class) {//标记该接口方法是kotlin的挂起函数isKotlinSuspendFunction = true;return null;}return result;
}
继续看HttpServiceMethod.parseAnnotations()
方法中,当判断isKotlinSuspendFunction
为true时,将返回一个SuspendForResponse
或者SuspendForBody
子类对象,至于到底返回哪一个就要看接口方法返回的数据类型是Response<User>
还是User,这涉及到最后对请求结果的Converter转换:
abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(Retrofit retrofit, Method method, RequestFactory requestFactory) {//是否为suspend挂起函数boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;boolean continuationWantsResponse = false;boolean continuationBodyNullable = false;Type adapterType;if (isKotlinSuspendFunction) {Type responseType = ...//判断接口方法返回值是否为 Response<T>的形式,比如suspend fun login(): Response<User>if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {// 如果是则需要拿到泛型User的类型,最后需要通过Converter转换结果数据为User对象responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);continuationWantsResponse = true;} else {//返回为其他类型,比如suspend fun login(): User}adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);}//接口适配器CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, adapterType, annotations);//响应数据转换器Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType);//OkHttpClient实例okhttp3.Call.Factory callFactory = retrofit.callFactory;if (!isKotlinSuspendFunction) { //非挂起函数的情况return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);//★★★以下为挂起函数的情况,根据挂起函数返回值类型不同区分} else if (continuationWantsResponse) {//如果接口定义:suspend fun login(): Response<User>return (HttpServiceMethod<ResponseT, ReturnT>)new SuspendForResponse<>(...);} else {//如果接口定义:suspend fun login(): Userreturn (HttpServiceMethod<ResponseT, ReturnT>)new SuspendForBody<>(...);}}
}
6.1 suspend接口的执行
调用接口动态代理对象的方法后,会调用loadServiceMethod().invoke()
,loadServiceMethod
会返回一个HttpServiceMethod
的子类对象,挂起函数则返回SuspendForResponse
或者SuspendForBody
类型,这里就拿SuspendForBody
为例,接下来的invoke()会创建一个OkHttpCall对象,并调用SuspendForBody
的adapt()
方法:
static final class SuspendForBody<ResponseT> extends HttpServiceMethod<ResponseT, Object> {private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;@Overrideprotected Object adapt(Call<ResponseT> call, Object[] args) {/*** 问题:一直没查到挂起函数接口对应的接口适配器callAdapter是那个类,挂起函数被编译后返回类型是Object(也就是结果值),* 默认的接口适配器是不支持的,如果直到的同学请q我** 只能确定的是这个接口适配器转换后的对象还是Call的子类*/call = callAdapter.adapt(call);//获取挂起函数的参数中的Continuation实参对象,这个对象表示协程中调用接口之后的代码,是接口返回数据后要接着调用的代码Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];try {return isNullable //跟踪代码,发现isNullable只能是false? KotlinExtensions.awaitNullable(call, continuation)//★★★将调用Call类的await()扩展方法: KotlinExtensions.await(call, continuation); } catch (Exception e) {return KotlinExtensions.suspendAndThrow(e, continuation);}}
}
adapt()
方法中首先对call进行了转换,转换之后的类型还是Call的子类,然后调用Call的kotlin扩展方法KotlinExtensions.await(call, continuation)
,await中直接调用了call.enqueue发起请求,然后在回调中将响应结果传给续体对象,resume恢复协程的执行:
/**Call的扩展方法,被定义在retrofit2.KotlinExtensions.kt文件中*/
suspend fun <T : Any> Call<T>.await(): T {return suspendCancellableCoroutine { continuation ->...//发起请求:相当于this.enqueue,而扩展方法中的this就是被扩展的类也就是call对象enqueue(object : Callback<T> {override fun onResponse(call: Call<T>, response: Response<T>) {if (response.isSuccessful) {val body = response.body()if (body == null) {...continuation.resumeWithException(e) //协程抛异常} else {//★返回响应结果,恢复协程执行continuation.resume(body)}} else {continuation.resumeWithException(HttpException(response))}}override fun onFailure(call: Call<T>, t: Throwable) {continuation.resumeWithException(t)}})}
}
7 Retrofit调用流程
8 Retrofit的设计模式分析
8.1 外观模式(Facade Pattern)
介绍:又称“门面模式”,隐藏系统的复杂性,并向客户端提供一个可以访问系统的接口,属于结构型模式
Retrofit框架不管是配置还是使用都将Retrofit
类作为唯一入口,对外提供简单统一的接口而隐蔽系统具体实现。很多工具库和开源库都使用了外观模式,比如Glide:
//Glide提供的统一访问入口为Glide类Glide.with(context).asBitmap() .load(uri).apply(options).into(imageView);
8.2 建造者模式(Builder Pattern)
介绍:使用多个简单的对象一步步构建成一个复杂的对象,将构建复杂对象的过程和它的部件解耦,使构建过程和部件的表示隔离
比如Retrofit对象的构建Retrofit.Builder().xxx.build()
,因为Retrofit维护了非常多的配置项,如果不通过建造者模式去构建而是用构造方法,则非常不方便
8.3 动态代理模式(Proxy Pattern)
介绍:一个类代表另一个类的功能,属于结构型模式。动态的意思是程序运行的时候才创建代理对象
使用Retrofit将服务器接口定义为抽象方法,并不存在真正的实现类,而是通过动态代理模式创建接口的动态代理对象控制接口方法的调用,在InvocationHandler.invoke()
中拦截接口方法统一交给ServiceMethod
及其子类处理,ServiceMethod
表示一个接口方法对象,具备解析接口方法注解、参数的能力(通过RequestFactory),invoke()
会返回接口方法对应的返回类型(通过接口适配器CallAdapter转换)
public <T> T create(final Class<T> service) {//创建接口的动态代理对象并返回return (T)Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] {service},new InvocationHandler() {@Overridepublic @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)throws Throwable {...//拦截代理对象方法(接口方法)的调用,统一调用loadServiceMethod(method).invoke(args)return platform.isDefaultMethod(method)? platform.invokeDefaultMethod(method, service, proxy, args): loadServiceMethod(method).invoke(args);}});}
8.4 静态代理模式(Proxy Pattern)
介绍:静态代理是在编码阶段就确定了代理类
Retofit提供的默认接口适配器会将OkHttpCall
转换成ExecutorCallbackCall
对象,ExecutorCallbackCall
就用到了静态代理模式(也可看作装饰者模式),它作为代理类控制委托类OkHttpCall的执行,目的是将请求回调切换到主线程。
//静态代理类,和委托类实现同一个接口static final class ExecutorCallbackCall<T> implements Call<T> {final Executor callbackExecutor; //通过Handler实现线程切换的执行器final Call<T> delegate; //OkHttpCall类型委托对象ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {this.callbackExecutor = callbackExecutor;this.delegate = delegate;}@Overridepublic void enqueue(final Callback<T> callback) {Objects.requireNonNull(callback, "callback == null");//调用委托类delegate.enqueue(new Callback<T>() {@Overridepublic void onResponse(Call<T> call, final Response<T> response) {//回调切换到主线程callbackExecutor.execute(() -> {if (delegate.isCanceled()) {callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));} else {callback.onResponse(ExecutorCallbackCall.this, response);}});}@Overridepublic void onFailure(Call<T> call, final Throwable t) {callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));}});}...}
8.5 装饰器模式(Decorator Pattern)
介绍:允许向一个现有的对象添加新的功能,同时又不改变其结构。属于结构型模式,它是作为现有的类的一个包装。
上面的ExecutorCallbackCall
也可看作是装饰器模式,包装原来的OkHttpCall对象,并对OkHttpCall进行功能增强,增加了切换主线程的功能。
装饰器模式和静态代理模式的共同特征是持有委托类的对象,并调用委托类方法。区别是装饰器模式的目的是功能增强,而静态代理目的是控制委托类的方法,但是这并没有一个明确的分界,比如ExecutorCallbackCall
中增加切换主线程的功能既可以说是对OkHttpCall
的功能增强,又可以说是控制它的方法访问。
8.6 工厂模式(Factory Pattern)
简单工厂模式
介绍:在创建对象时不会对客户端暴露创建逻辑,并使用同一个共同的接口指向被创建的对象,主要解决接口选择的问题,提供程序扩展性,属于创建型模式。工厂模式三种角色(Factory工厂类、Product产品抽象类、Product的具体子类)
Retrofit通过Builder在构建实例时,通过Platform
来添加平台默认支持(比如添加Android平台默认的接口适配器),通过静态方法get()获取当前平台对象,findPlatform()
就是一个静态工厂方法,该方法会创建出不同的Platform子类对象。Platform本身即作为产品接口,同时又是一个工厂类。简单工厂模式最显著的特征就是只有一个工厂Factory类,用于生产不同产品
class Platform {private static final Platform PLATFORM = findPlatform();static Platform get() {return PLATFORM;}private static Platform findPlatform() {return "Dalvik".equals(System.getProperty("java.vm.name"))? new Android() //: new Platform(true);}static final class Android extends Platform {}
}
工厂模式
简单工厂模式中的工厂方法类只有一个,比如上面的Platform
,如果现在需要增加其他平台的支持(Windows、Ipad)则需要修改工厂方法findPlatform()
,这不符合开闭原则(对扩展开放,对修改封闭)。如果将工厂类Factory的公共部分抽象出来,让每种产品都对应自己的Factory子类,需要不同产品就调用不同的工厂类生产,这就是工厂模式。如果添加新产品的支持,只需要创建一个子Factory。工厂模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品
为了让Retrofit支持各种接口类型,提供了CallAdapter对Call进行适配,不同的接口对应不同的适配器对象,而具体使用哪种适配器是CallAdapter.Factory
负责生产的,它有一个抽象的get()
方法,如果接口返回Call,则由默认工厂DefaultCallAdapterFactory
生产适配器,如果接口返回Observable,则由RxJava3CallAdapterFactory
生产适配器,如果以后需要支持其他的接口返回类型,只需要新增一个接口适配器CallAdapter,然后为其创建一个工厂类重写get()生产该适配器对象即可,而不需要修改Retrofit框架的代码
/**产品接口*/
public interface CallAdapter<R, T> {Type responseType();T adapt(Call<R> call);/**工厂抽象类,这个工厂只生产一种产品*/abstract class Factory {//唯一的工厂方法,用于生产一种产品public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit);//工厂类的两个公共工具方法protected static Type getParameterUpperBound(int index, ParameterizedType type) {return Utils.getParameterUpperBound(index, type);}protected static Class<?> getRawType(Type type) {return Utils.getRawType(type);}}
}
抽象工厂模式
相比于工厂模式,具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象,例如:Converter.Factory需要同时提供请求参数数据和返回数据的转换类,就需要考虑抽象工厂模式。
/**产品接口*/
public interface Converter<F, T> {@NullableT convert(F value) throws IOException;/**工厂抽象类,这个工厂可以生产多种产品*/abstract class Factory {//生产一个请求结果数据转换器对象public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {return null;}//生产一个请求参数数据转换器对象public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,...) {return null;}//生产一个字符串转换器对象public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) {return null;}//工厂类的两个公共工具方法protected static Type getParameterUpperBound(int index, ParameterizedType type) {return Utils.getParameterUpperBound(index, type);}protected static Class<?> getRawType(Type type) {return Utils.getRawType(type);}}
}
为了让Retrofit和okhttp对接,Retrofit提供了数据转换器Converter,可以通过抽象工厂模式的工厂类获取不同类型的转换器对象(请求参数转换器or响应结果转换器)。为什么要使用抽象工厂模式?使用普通工厂模式不能实现吗?是可以实现的,但是客户端配置就变成下面的样子了:
//普通工厂模式实现Converter转换器val retrofit = Retrofit.Builder()...//分别配置请求和响应数据的转换器工厂.addRequestConverterFactory(RequestConverterFactory()).addResponseConverterFactory(ResponseConverterFactory()).build()//对比抽象工厂模式val retrofit = Retrofit.Builder()...//只需要配置一个转换器工厂,这个工厂既可以生产请求数据转换器,又能生产响应数据转换器.addConverterFactory(XxxConverterFactory()).build()
8.7 适配器模式(Adapter Pattern)
介绍:作为两个不兼容的接口之间的桥梁,属于结构型模式,它结合了两个独立接口的功能。
CallAdapter和Converter使用到了适配器模式:
- 接口适配:Retrofit构建的Call对象是
OkHttpCall
类型的,但是接口支持返回Call,还有Observable等,CallAdapter就是这两种不同类型之间的桥梁,目的是将OkHttpCall
类型对象转换为接口返回类型对象。下次如果出来一个新技术,需要支持返回一种新类型,只需要编写一个新的适配器,并配置给Retrofit就可以了 - 请求参数&响应数据适配:Retrofit的请求参数和OKHttp的Request不兼容,okHttp的响应数据和Retrofit也不兼容,Converter就充当了Retrofit和OkHttp之间数据的桥梁,完成数据的转换
8.8 策略模式(Strategy Pattern)
介绍:一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的context对象。策略对象改变context对象的执行算法。
策略模式中包含三种角色:
- Strategy抽象策略:策略抽象类
- Strategy策略子类:实现了Strategy,对应不同的策略
- Context上下文:表示操作策略的上下文
Retrofit中的addCallAdapterFactory()
和addConverterFactory()
用于配置策略。以addCallAdapterFactory()
为例,配置的接口适配工厂对象就是策略上下文Context,CallAdapter
对应策略抽象,不同的接口适配器都实现了CallAdapter.adapt()
方法,配置不同的工厂会生产不同的策略实现类,不同策略实现类的adapt()行为不同。
关于CallAdapter
和CallAdapter.Factory
相关的设计模式有:工厂模式、适配器模式、策略模式,这里很容易混淆,那是因为没有弄清关注点:
工厂模式应该关注的是Factory的get方法:
abstract class Factory {//工厂模式生产一个对应的接口适配器对象public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit);}
适配器模式关注点是CallAdapter的adapt()
方法实现call的转换以适配接口方法的返回类型:
public interface CallAdapter<R, T> {//将OkHttpCall转换为接口方法的返回类型T adapt(Call<R> call);
}
策略模式模式关注点是通过addCallAdapterFactory()
配置策略上下文,从而创建不同的CallAdapter
子类,不同子类的adapt()行为不同:
val retrofit = Retrofit.Builder()/**配置接口适配策略上下文*/.addCallAdapterFactory(RxJava3CallAdapterFactory.create()).build()/**策略抽象类*/
public interface CallAdapter<R, T> {T adapt(Call<R> call);
}/**不同的策略子类*/
//Android默认策略实现
new CallAdapter<Object, Call<?>>() {public Type responseType() {return responseType;}//不同的行为public Call<Object> adapt(Call<Object> call) {return (Call)(executor == null ? call : new DefaultCallAdapterFactory.ExecutorCallbackCall(executor, call));}}
//RxJava的策略实现
final class RxJava3CallAdapter<R> implements CallAdapter<R, Object> {//不同的行为@Overridepublic Object adapt(Call<R> call) {Observable<Response<R>> responseObservable =isAsync ? new CallEnqueueObservable<>(call) : new CallExecuteObservable<>(call);Observable<?> observable;if (isResult) {observable = new ResultObservable<>(responseObservable);} else if (isBody) {observable = new BodyObservable<>(responseObservable);} else {observable = responseObservable;}...}
}
8.9 观察者模式(Observer Pattern)
介绍:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。观察者模式中的关注点是观察者订阅被观察者,当被观察者状态变化时通知观察者,即便只存在一个观察者
Retrofit支持的RxJava就使用了观察者模式,不仅如此,调用OkHttpCall对象的enqueue()
方法时,使用了CallBack回调,其实回调也是一种观察者模式。这个示例中,Call是被观察者(Subject)的抽象,OkHttpCall是被观察者的具体实现,enqueue()
方法表示订阅,Callback是观察者(Observer)的抽象,调用enqueue()
方法时传入的回调实参就是观察者实现类:
/**被观察者*/
final class OkHttpCall<T> implements Call<T> {//可被订阅@Overridepublic void enqueue(final Callback<T> callback/*观察者*/) {okhttp3.Call call;synchronized (this) {...call = createRawCall();call.enqueue(new okhttp3.Callback() {@Overridepublic void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {Response<T> response = parseResponse(rawResponse);...//通知观察者状态变化callback.onResponse(OkHttpCall.this, response);}...});}}
8.10 原型模式(Prototype Pattern)
介绍:属于创建型模式,主要用于复制一个对象,使用原型模式创建对象比直接new一个对象在性能上好很多,因为Object的clone()是一个本地方法,可以直接操作内存。原型模式的另一个好处是简化对象创建,如果需要在一段代码中多次创建一个对象不妨使用原型模式。原型模式通常需要实现Cloneable接口
表示对象是可复制的。
使用原型模式复制对象需要主要深拷贝与浅拷贝的问题。Object类的clone方法只会拷贝对象中的基本的数据类型,对于数组、集合容器、引用对象等不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝
Retrofit中的OkHttpCall就使用了原型模式用于重复对象的复制创建,虽然clone中也是new了一个对象,但是该对象相关属性都是原来对象的属性。这里是通过浅拷贝,原OkHttpCall对象中的引用对象是通过构造方法赋值的:
//interface Call<T> extends Cloneable
final class OkHttpCall<T> implements Call<T> {...//★★★★Call接口继承Cloneable , OkHttpCall实现了clone()方法@Overridepublic OkHttpCall<T> clone() {//浅拷贝:只拷贝了Object中的基本类型数据return new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);}
}//Android平台默认的Call实现
static final class ExecutorCallbackCall<T> implements Call<T> {final Call<T> delegate; //delegate就是OkHttpCall类型的@Override public Call<T> clone() {//★★★★delegate.clone()使用了原型模式复制对象return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());}
}
8.11 享元模式(Flyweight Pattern)
介绍:享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象,用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式
说的简单一点就是,一段程序中经常需要创建某种类型的对象,而对象的数量是有限的,可以将已经创建的对象缓存起来,下次要创建这个对象时直接从缓存中拿,缓存中没有就去创建。Retrofit中的ServiceMethod就用到了享元模式:
public final class Retrofit {private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();//调用接口方法时,使用享元模式尝试从缓存中获取,这样避免一个接口被调用多次时每次都要解析接口方法的注解和参数ServiceMethod<?> loadServiceMethod(Method method) {//取缓存ServiceMethod<?> result = serviceMethodCache.get(method);if (result != null) return result;synchronized (serviceMethodCache) {result = serviceMethodCache.get(method);if (result == null) {//创建并缓存result = ServiceMethod.parseAnnotations(this, method);serviceMethodCache.put(method, result);}}return result;}
}
8.12 单例模式(Singleton Pattern)
介绍:单例模式为一个类提供了访问其唯一对象的方法,确保内存中只会存在该类的唯一对象,属于创建型模式
懒汉单例模式的优点是单例只要有在使用是才被实例化,缺点是getInstance()是同步方法,造成不必要的同步开销。DCL(Double Check Lock)是对懒汉单例模式的升级,getInstance方法对instance进行了两次判空,第一层判断是为了避免不必要的同步,第二层判断是为了在null时创建实例,这里涉及到对象实例化过程的原子问题。在Java中,创建对象并非原子操作,而是包含分配内存、初始化成员字段、引用指向等一连串操作,而多线程环境下,由于指令重排序的存在,初始化指令和引用指令可能是颠倒,那么可能当线程执行第一个判断不为null返回的对象,却是未经初始化的(别的对象创建Singleton时,初始化指令和引用指令颠倒了)。
public class Singleton {private static Singleton instance;private Singleton() {}//饿汉式的单例模式static final StreamingResponseBodyConverter INSTANCE = new StreamingResponseBodyConverter();//DCLpublic static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}//静态内部类public static Singleton getInstance() {return SingletonHolder.instance;}private static class SingletonHolder {private static final Singleton instance = new Singleton();}
}
Retrofit在build时,添加的Android平台默认的数据转换器工厂就是饿汉式的单例模式:
public Retrofit build() {...//添加默认数据转换器工厂converterFactories.add(new BuiltInConverters());...}}final class BuiltInConverters extends Converter.Factory {@Overridepublic @Nullable Converter<ResponseBody, ?> responseBodyConverter(...) {//返回数据转换器单例对象if (type == ResponseBody.class) {return Utils.isAnnotationPresent(annotations, Streaming.class)? StreamingResponseBodyConverter.INSTANCE: BufferingResponseBodyConverter.INSTANCE;}...return null;}static final class StreamingResponseBodyConverterimplements Converter<ResponseBody, ResponseBody> {//★★★饿汉式的单例模式static final StreamingResponseBodyConverter INSTANCE = new StreamingResponseBodyConverter();@Overridepublic ResponseBody convert(ResponseBody value) {return value;}}...}
Retrofit流程及设计模式全解析相关推荐
- 软件工程23种设计模式全解析
23种设计模式全解析 一.设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代 ...
- 23种设计模式全解析
longyulu的专栏 目录视图 摘要视图 订阅 [公告]博客系统优化升级 [收藏]Html5 精品资源汇集 博乐招募开始啦 id="cpro_u2392861_ifra ...
- 设计模式全解析 23种
一.设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接 ...
- 设计模式全解析(一)——带你搞懂设计模式
各位朋友,一提到"设计模式"四个字,是不是有一种若隐若现的朦胧感,"我知道设计模式,我知道单例.工厂.观察者.策略...",但是设计模式到底是什么呢?解决了什么 ...
- 淘宝小部件 Canvas 渲染流程与原理全解析
作者:史健平(楚奕) 上篇回顾:<淘宝小部件:全新的开放卡片技术!>.<淘宝小部件在 2021 双十一中的规模化应用> 本文主要从技术视角阐述 Canvas 在小部件下的渲染原 ...
- Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/53939176 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭 ...
- Java 面试全解析:核心知识点与典型面试题
课程背景 又逢"金九银十",年轻的毕业生们满怀希望与忐忑,去寻找.竞争一个工作机会.已经在职的开发同学,也想通过社会招聘或者内推的时机争取到更好的待遇.更大的平台. 然而,面试人群 ...
- 超全的Android面经_安卓面经(20/30)之自定义View全解析
系列专栏: 安卓高频面经解析大全专栏链接:150道安卓高频面试题全解析 安卓高频面经解析大全目录详情 : 安卓面经_anroid面经_150道安卓常见基础面试题全解析 安卓系统Framework面经专 ...
- Retrofit-原理全解析
1.Retrofit介绍 Retrofit是一个RESTful的Http网络请求框架的封装,网络请求部分本质是由OKHttp完成的 而我们学习Retrofit除了对Http请求有进一步的了解之外,我们 ...
最新文章
- 南通工学院计算机系97顾月,南通大学电气工程学院
- cocos2dx游戏开发——微信打飞机学习笔记(三)——WelcomeScene的搭建
- 红帽子linux6.6内核版本,RedHat/CentOS发行版本号及内核版本号对照表
- java 修改ip_如何用脚本快速修改IP地址(Netsh)
- 安徽新华学院计算机设计大赛,第十届中国大学生计算机设计大赛在安徽新华学院开幕...
- excel怎么下方方格子_Excel重复值的坑,你踩过几个?
- 【IP 地址 和 端口 _简介】
- 最新仿企业发卡源码,自动发卡平台
- 修改状态栏字体颜色和背景颜色
- 金蝶账套总显示找不到服务器,金蝶KIS账套名称登录时没有显示怎么办呢
- java digester_Apache Commons Digester
- openstack源码架构_openstack创建虚拟机源码阅读
- ubutu 识别android手机
- 动词ing基本用法_(完整版)英语动词ing的用法
- Mybatis和MybatisPlus3.4的使用
- 推荐一个好玩网站,黑白照片上色、人脸识别都不是问题!
- java for二重循环_java什么是二重循环
- 基于php的旅游景点网站设计
- smbms(超市管理系统)源码 + 分析
- shader graph_在Shader Graph中使用表面梯度框架进行法线贴图合成