4.从架构角度看Retrofit的做用、原理和启示
3.Retrofit 框架源码学习
2.Retrofit分析-经典设计模式案例
1.Retrofit分析-漂亮的解耦套路php
=================css
4.从架构角度看Retrofit的做用、原理和启示java
Retrofit是squareup公司的开源力做,和同属squareup公司开源的OkHttp,一个负责网络调度,一个负责网络执行,为Android开发者提供了即方便又高效的网络访问框架。android
不过,对于Retrofit这样设计精妙、代码简洁、使用方便的优秀开源项目,不能仅知道如何扩展和使用,或者仅研究它采用的技术或模式,git
“技”固然重要,但不能忽视了背后的“道”。github
对于Retrofit,咱们还应该看到的,是她在优化App架构方面的努力,以及她在提高开发效率方面的借鉴和启示。算法
本文试图经过一个具体场景,先总结Retrofit在架构中起到的做用,再分析其实现原理,最后探讨Retrofit给咱们带来的启示。数据库
咱们先经过一个简单的应用场景来回顾Retrofit的使用过程。编程
一般来讲,使用Retrofit要通过这样几个步骤json
compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:retrofit-converters:2.3.0' compile 'com.squareup.retrofit2:retrofit-adapters:2.3.0'
若是须要使用更多扩展功能,好比gson转换,rxjava适配等,能够视本身须要继续添加引用
compile 'com.squareup.retrofit2:converter-gson:2.3.0' compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
若是现有的扩展包不能知足须要,还能够本身扩展converter,adapter等。
public interface INetApiService { @GET("/demobiz/api.php") Call<BizEntity> getBizInfo(@Query("id") String id); }
在这个接口定义中,用注解@GET("/demobiz/api.php")声明了url路径,用注解@Query("id") 声明了请求参数。
最重要的是,用Call<BizEntity>声明了返回值是一个Retrofit的Call对象,而且声明了这个对象处理的数据类型为BizEntity,BizEntity是咱们自定义的数据模型。
//新建一个Retrofit对象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN)//要访问的网络地址域名,如http://www.zhihu.com .addConverterFactory(GsonConverterFactory.create()) .build(); ... //用retrofit加工出对应的接口实例对象 INetApiService netApiService= retrofit.create(INetApiService.class); //能够继续加工出其余接口实例对象 IOtherService otherService= retrofit.create(IOtherService.class); ··· //调用接口函数,得到网络工做对象 Call<BizEntity> callWorker= netApiService.getBizInfo("id001");
这个复杂的过程下来,最终获得的callWorker对象,才能够执行网络访问。
callWorker.enqueue(new Callback<BizEntity>() { @Override public void onResponse(Call<BizEntity> call, Response<BizEntity> response) {...} @Override public void onFailure(Call<BizEntity> call, Throwable t) {...} });
在回调函数里,取得咱们须要的BizEntity数据对象。
网络访问结束。
咱们从上面的应用场景能够看出,Retrofit并不作网络请求,只是生成一个能作网络请求的对象。
Retrofit的做用是按照接口去定制Call网络工做对象。
什么意思?就是说:
Retrofit不直接作网络请求
Retrofit不直接作网络请求
Retrofit不直接作网络请求
重要的事情说三遍。
网络请求的目标虽然是数据,可是咱们须要为这个数据写大量的配套代码,发起请求的对象Call,接收数据的对象CallBack,作数据转换的对象Converter,以及检查和处理异常的对象等。
这对于一个项目的开发、扩展和维护来讲,都是成本和风险。
而Retrofit作的事情,就是为开发者节省这部分的工做量,Retrofit一方面从底层统一用OkHttp去作网络处理;
另外一方面在外层灵活提供能直接融入业务逻辑的Call网络访问对象。
具体来讲,Retrofit只负责生产对象,生产能作网络请求的工做对象,他有点像一个工厂,只提供产品,工厂自己不处理网络请求,产品才能处理网络请求。
Retrofit在网络请求中的做用大概能够这样理解:
咱们看到,从一开始,Retrofit要提供的就是个Call工做对象。
换句话说,对于给Retrofit提供的那个接口
public interface INetApiService { @GET("/demobiz/api.php") Call<BizEntity> getBizInfo(@Query("id") String id); }
这个接口并非传统意义上的网络请求接口,这个接口不是用来获取数据的接口,而是用来生产对象的接口,这个接口至关于一个工厂,接口中每一个函数的返回值不是网络数据,而是一个能进行网络请求的工做对象,咱们要先调用函数得到工做对象,再用这个工做对象去请求网络数据。
因此Retrofit的实用价值意义在于,他能根据你的接口定义,灵活地生成对应的网络工做对象,而后你再择机去调用这个对象访问网络。
理解了这一点,咱们才能去扩展Retrofit,并理解Retrofit的设计思想。
咱们先来看Retrofit能扩展哪些功能,而后再去理解Retrofit的工做原理。
Retrofit主要能够扩展三个地方:
OkHttpClient mClient = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { try { Request.Builder builder = chain.request().newBuilder(); builder.addHeader("Accept-Charset", "UTF-8"); builder.addHeader("Accept", " application/json"); builder.addHeader("Content-type", "application/json"); Request request = builder.build(); return chain.proceed(request); } catch (Exception e) { e.printStackTrace(); } return null; } }).build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .client(mClient) .build();
扩展的是对返回的数据类型的自动转换,把一种数据对象转换为另外一种数据对象。
在上述场景中,GsonConverterFactory能够把Http访问获得的json字符串转换为Java数据对象BizEntity,这个BizEntity是在INetApiService接口中要求的的。
这种转换咱们本身也常常作,很好理解。
若是现有的扩展包不能知足须要,能够继承Retrofit的接口。retrofit2.Converter<F,T>,本身实现Converter和ConverterFactory。
在建立Retrofit对象时,能够插入咱们自定义的ConverterFactory。
//retrofit对象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(YourConverterFactory.create())//添加自定义Converter .build();
扩展的是对网络工做对象callWorker的自动转换,把Retrofit中执行网络请求的Call对象,转换为接口中定义的Call对象。
这个转换不太好理解,咱们能够对照下图来理解:
Retrofit自己用一个OkHttpCall的类负责处理网络请求,而咱们在接口中定义须要定义不少种Call,例如Call<BizEntity>,或者Flowable<BizEntity>等,接口里的Call和Retrofit里的OkHttpCall并不一致,因此咱们须要用一个CallAdapter去作一个适配转换。
(Retrofit底层虽然使用了OkHttpClient去处理网络请求,但她并无使用okhttp3.call这个Call接口,而是本身又建了一个retrofit2.Call接口,OkHttpCall继承的是retrofit2.Call,与okhttp3.call只是引用关系。
这样的设计符合依赖倒置原则,能够尽量的与OkHttpClient解耦。)
这实际上是Retrofit很是核心,也很是好用的一个设计,若是咱们在接口中要求的函数返回值是个RxJava的Flowable对象
public interface INetApiService { @GET("/demobiz/api.php") Flowable<BizEntity> getBizInfo(@Query("id") String id); }
那么咱们只须要为Retrofit添加对应的扩展
//retrofit对象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build();
就能获得Flowable类型的callWorker对象
//用retrofit加工出对应的接口实例对象 INetApiService netApiService= retrofit.create(INetApiService.class); ··· //调用接口函数,得到网络工做对象 Flowable<BizEntity> callWorker= netApiService.getBizInfo("id001");
在这里,callAdapter作的事情就是把retrofit2.Call对象适配转换为Flowable<T>对象。
一样,若是现有的扩展包不能知足须要,能够继承Retrofit的接口retrofit2.CallAdapter<R,T>,本身实现CallAdapter和CallAdapterFactory。
Retrofit当然设计精妙,代码简洁,使用方便,但相应的,咱们要理解Retrofit的实现原理也不太容易,这么精妙的设计是极佳的研究素材,咱们不能仅仅停留在知道怎么使用,怎么扩展的阶段,那实在是对这个优秀开源项目的浪费。
其实,Retrofit使用的,就是动态代理,方法注解、建造者和适配器等成熟的技术或模式,可是因为她的设计紧凑,并且动态代理屏蔽了不少过程上的细节,因此比较难以理解。
从前面的使用场景可知,retrofit会生成一个接口实例。
//用retrofit加工出对应的接口实例对象 INetApiService netApiService= retrofit.create(INetApiService.class);
到Retrofit源码里看create函数,是一个动态代理。
public <T> T create(final Class<T> service) { ... return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { ... ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); } }); }
要理解动态代理,最好要看到动态生成的代理类。
因为动态代理是在运行时动态生成的代理类,用常规的反编译方法没法查看,通常要使用Java提供的sun.misc.ProxyGenerator.generateProxyClass(String proxyName,class[] interfaces)函数生成代理类,函数会返回byte[]字节码,而后对字节码反编译获得Java代码。
有一个小问题是,AndroidStudio并不提供sun.misc这个包,咱们须要用IntelliJ或者Eclipse创建一个Java工程,在Java环境里调用这个函数。
拿到的代理类,大概是这样的:
public final class INetApiService extends Proxy implements INetApiService { ...//一些Object自带方法 private static Method m3;//接口定义的方法 static { try { //Object自带方法的初始化 m0,m1,m2 = ... //接口中定义的方法 m3 = Class.forName("com.demo.net$INetApiService")//反射接口类 .getMethod("getBizInfo",//反射函数 new Class[] { Class.forName("java.lang.String") });//反射参数 //接口中定义的其余方法 ... } ... } //返回接口实例对象 public INetApiService (InvocationHandler invocationHandler){ super(invocationHandler); } // public final Call getBizInfo(String str){ ... try{//用Handler去调用 return (Call)this.h.invoke(this, m3, new Object[]{str}); } } }
咱们能够看到,代理类生成的是一个INetApiService接口的实例对象,该对象的getBizInfo函数返回的是接口中定义的Call网络工做对象,
这也体现了Retrofit的核心价值,生成接口定义的Call网络工做对象。
那么,这个Call网络工做对象是如何生成的呢,上面动态代理生成的代码是这样的:
return (Call)this.h.invoke(this, m3, new Object[]{str});
也就是说,这个Call网络工做对象是在InvocationHandler中实现的,也就是在Retrofit.create函数中,由InvocationHandler实现的。
这样咱们就明白了,Retrofit使用动态代理,实际上是为了开发者在写代码时方便调用,而真正负责生产Call网络工做对象的,仍是Retrofit.create函数中定义的这个InvocationHandler,这个InvocationHandler的代码咱们再贴一遍:
new InvocationHandler() { ... ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); }
ServiceMethod能让咱们准确解析到INetApiService中定义的函数,为最后的适配转换提供转换目标,详细分析咱们后面再说,先看适配转换的过程。
咱们看到,Retrofit内部默认使用OkHttpCall对象去处理网络请求,可是返回的网络工做对象是通过适配器转换的,转换成接口定义的那种Call网络工做对象。
这个适配转换,就是Retrofit能按照接口去定制Call网络工做对象的秘密。
咱们在初始化Retrofit对象时,好像不添加CallAdapterFactory也能实现适配转换。
//retrofit对象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) //能够不添加CallAdapterFactory .build();
这是怎么回事呢,咱们知道Retrofit使用了建造者模式,建造者模式的特定就是实现了建造和使用的分离,因此建造者模式的建造函数里,
通常会有很复杂的对象建立和初始化过程,因此咱们要看一下Retrofit的build函数。
public Retrofit build() { ... okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { callFactory = new OkHttpClient();//使用OkHttpClient处理网络请求 } ... //根据当前运行平台,设置默认的callAdapterFactory adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); ... return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); }
这段代码里,咱们看到Retrofit使用OkHttpClient处理网络请求,而且会添加默认的callAdapterFactory,这个platform是一个简单工厂,能根据当前系统平台去生成对应的callAdapterFactory
private static Platform findPlatform() { try { Class.forName("android.os.Build"); if (Build.VERSION.SDK_INT != 0) { return new Android();//根据当前系统平台返回相应的对象 } ... } ... static class Android extends Platform { ... @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) { if (callbackExecutor == null) throw new AssertionError(); return new ExecutorCallAdapterFactory(callbackExecutor); } ... }
这个Platform是Retrofit在Builder的构造函数里初始化的。
因此,在Retrofit.build()函数中,咱们为Retrofit默认添加的callAdapterFactory,是在Platform中为Android系统设定的ExecutorCallAdapterFactory。
咱们看ExecutorCallAdapterFactory的代码,这是一个工厂类,能够返回CallAdapter对象:
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { ... return new CallAdapter<Object, Call<?>>() { ... // 转换后 转换前,也就是OkHttpCall @Override public Call<Object> adapt(Call<Object> call) { return new ExecutorCallbackCall<>(callbackExecutor, call); } }; }
在adapt函数中,适配器会把Retrofit中用来访问网络的OkHttpCall,转换为一个ExecutorCallbackCall(继承了INetApiService接口里要求返回的网络工做对象retrofit2.Call),
这个例子里面,因为OkHttpCall和ExecutorCallbackCall都实现了retrofit2.Call接口,结果出现了从Call<Object>转换为Call<Object>的状况,这可能不容易理解,咱们换个RxJava2CallAdapterFactory来看看
//RxJava2CallAdapterFactory中 @Override public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { ... return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable, isSingle, isMaybe, false); } //RxJava2CallAdapter中 // 转换后 转换前,也就是OkHttpCall @Override public Object adapt(Call<R> call) { ... Observable<?> observable; ... return observable; }
这个CallAdapter的转换就比较明显了,把retrofit2.Call对象经过适配器转换为了一个实为Observable<?>的Object对象。
至此,咱们能够理解Retrofit根据接口定义动态生产Call网络请求工做对象的原理了,其实就是经过适配器把retrofit2.Call对象转换为目标对象。
至于适配器转换过程当中,如何实现的对象转换,就能够根据需求来自由实现了,好比利用静态代理等,若有必要,咱们能够自行开发扩展,Retrofit框架并不限制咱们对于适配器的实现方式。
在前面分析中,咱们知道了Retrofit的总体工做流程,就是Retrofit用动态代理生成Call网络请求对象,在这个过程当中,用适配器把Retrofit底层的retrofit2.Call对象转换为INetApiService中定义的Call网络请求对象(如Flowable)。
问题是,Retrofit具体是如何知道了INetApiService中定义的Call网络请求对象,如何实现网络请求,以及如何执行的数据转换呢?
具体过程以下;
首先,根据INetApiService中定义的函数,解析函数,获得函数的具体定义,并生成对应的ServiceMethod。
而后,根据这个ServiceMethod,实现一个OkHttpCall的Call对象,负责在Retrofit底层实现网络访问。
其中,在网络访问返回了网络数据时,根据ServiceMethod实现数据转换。
最后,利用上一小节中匹配的适配器,把OkHttpCall对象转换为INetApiService要求的Call网络请求对象。
因此,咱们要了解的就是函数解析、网络请求和数据转换这三个动做,至于最后的适配转换,在上一节中已经分析过了。
1. 函数解析
在接口函数里,用注解描述了输入参数,用Java对象定义了返回值类型,因此对输入参数和返回值,ServiceMethod采起了不一样的方式去处理。
输入参数
输入参数是用来描述url的,它的处理相对简单,ServiceMethod会根据反射获得的Method,取得Annotation注解信息,这些注解是Retrofit本身预约义好的(retrofit2.http.*),ServiceMethod根据预先的定义,直接判断注解所属的逻辑分支,在有网络请求时分状况进行处理,就能获得目标url,http请求头等数据。
返回值
返回值是须要用CallAdapter去适配的,因此核心在于生成对应的CallAdapter。
在Retrofit生成Call网络工做对象时,她经过动态代理获取到了接口函数的Method定义,从这个Method中能够获取函数定义的返回对象类型,因为这个转换是须要CallAdapterFactory生产CallAdapter对象去实现,而Retrofit事先并不知道要使用哪一个Factory,因此她是遍历全部的CallAdapterFactory,根据目标函数的返回值类型,让每一个Factory都去尝试生产一个CallAdapter,哪一个成功就用哪一个。
2. 网络请求
OkHttpCall继承的retrofit2.Call接口是为了依赖倒置解耦的,真正的网络请求是由OkHttpCall内部引用的okhttp3.call处理的,这个okhttp3.call是
借道ServiceMethod获取的Retrofit中的callFactory,也就是Retrofit中的OkHttpClient。
整个引用链条是这样的:
OkHttpCall--okhttp3.call
-->
ServiceMethod--callFactory
-->
Retrofit.build()--callFactory//(如未扩展赋值)new OkHttpClient();
-->
Retrofit.Builder().client(mClient)//(可能有扩展赋值)扩展过的OkHttpClient
最终的网络请求是由OkHttpCall调用OkHttpClient发出的,调用和回调等过程,也就是在OkHttpCall中处理的。
网络请求的生成过程当中,为了使用接口函数中定义的参数,OkHttpCall会调用ServiceMethod来生成Request请求对象,再交给OkHttpCall去处理。
3. 数据转换
由于回调是在OkHttpCall中处理的,因此对回调数据的转换也在OkHttpCall中触发,为了符合接口函数中定义的返回数据类型,OkHttpCall会调用ServiceMethod来转换Response返回数据对象。
OkHttpCall对返回的网络数据,会调用一个serviceMethod.toResponse(ResponseBody body)函数,函数中执行的是:
R toResponse(ResponseBody body) throws IOException { return responseConverter.convert(body); }
这个函数能够把原始的okhttp3. ResponseBody数据转换为INetApiService接口中要求的数据类型(如BizEntity类型)。
从代码能够看出,实现数据转换的核心对象实际上是responseConverter,这个Converter实际上要依次通过Retrofit的建造和ServiceMethod的建造后,才能肯定下来的。
Retrofit建造时添加数据转换工厂
Retrofit里有converterFactries列表,这是在咱们初始化Retrofit实例时添加的
//retrofit对象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(YourConverterFactory.create())//添加自定义Converter .build();
ServiceMethod建造时设定数据转换器
ServiceMethod在建造时,就已经肯定了对应的是INetApiService中的哪一个函数,因此须要明确设定本身的Converter<R,T>转换对象
public ServiceMethod build() { ... responseConverter = createResponseConverter(); ... }
这须要调用Retrofit
private Converter<ResponseBody, T> createResponseConverter() { ... retrofit.responseBodyConverter(responseType, annotations); }
Retrofit会在本身的转换器工厂列表中遍历每一个ConverterFactory,尝试根据ServiceMethod所对应的目标数据类型,找到Converter数据转换类
for (int i = start, count = converterFactories.size(); i < count; i++) { Converter<ResponseBody, ?> converter = converterFactories.get(i).responseBodyConverter(type, annotations, this); if (converter != null) { //noinspection unchecked return (Converter<ResponseBody, T>) converter; } }
以Gson转换为例,GsonConverterFactory会经过getAdapter来尝试匹配目标数据类型:
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {...}
若是能够匹配,那么前面调用serviceMethod.toResponse(ResponseBody body)函数时,会调用
R toResponse(ResponseBody body) throws IOException { return responseConverter.convert(body); }
在调用这段代码时,其实就是调用了Gson中最终执行数据转换的代码:
@Override public T convert(ResponseBody value) throws IOException { JsonReader jsonReader = gson.newJsonReader(value.charStream()); try { return adapter.read(jsonReader); } finally { value.close(); } }
总结来讲,Retrofit在类的单一职责方面分隔的很好,OkHttpCall类只负责网络交互,凡是须要知道函数定义的,都交给ServiceMethod类去处理,而ServiceMethod类对使用者不公开,由于Retrofit是个外观模式,而全部须要扩展的都在Retrofit的建造者中实现,他们的分工大概是这样的:
这三个类分工合做,共同实现了函数解析、网络访问和数据转换,并保留了良好的可扩展性。
至此,Retrofit的实现细节就已经基本清楚了,他用动态代理去定制接口定义的Call网络工做对象,用适配器去把底层的Call对象转换为目标Call对象,用函数解析/OkHttpClient/数据转换等实现对Call对象的适配转换,并能处理真正的网络请求。
这里面涉及的总体结构和角色分工,大概能够这样表示:
其中,扩展适配器、扩展数据转换和扩展OkHttpClient,虽然都是经过Retrofit实现扩展,但真正的使用者是Retrofit内部的ServiceMethod、OkHttpCall和okhttp3.call等类或对象。
若是咱们不直接正面分析Retrofit的结构设计和技术细节,而是先从Retrofit的功能和做用入手,倒过来推测Retrofit的目标,进而分析其架构和搭建细节,Retrofit为何会设计成这样就很好理解了。
Retrofit的功能是按照接口定义,自动定制Call网络工做对象,因此Retrofit的目标应该就是避免为网络访问开发大量的配套代码。
为了实现这一目标,Retrofit须要分析哪些是易变的,哪些是不变的,而后分别处理。
因为Retrofit提供网络访问的工做对象,又是服务于具体业务,因此能够分网络访问和具体业务两部分来分析。
网络访问的不变性
对于网络访问来讲,不变的是必定有一个实现网络访问的对象,Retrofit选用了自家的OkHttpClient,不过为了把Retrofit和OkHttp两个项目解耦合,Retrofit根据依赖倒置原则,定义了Retrofit本身的Call即retrofit2.call,并定义了操做网络请求的OkHttpCall。
网络访问的易变性
对于网络访问来讲,易变的是网络访问的url、请求方式(get/post等)、Http请求的Header设置与安全设置等,以及返回的数据类型。
针对易变的url和请求方式,Retrofit使用了方法注解的方式,可读性良好,扩展性优异,但这须要实现对接口函数中注解的解析,这样就有了ServiceMethod。
针对Http请求的各类设置,其实Retrofit没作什么,由于Retrofit使用的OkHttp有拦截器机制,能够应付这种变化。
针对返回的数据类型,因为目标数据类型与业务有关,是不肯定的,Retrofit没法提供一个万能的转换类,因此Retrofit提供了扩展接口,容许开发者本身定义ConverterFactory和Converter,去实现潜在的数据类型转换。
具体业务的不变性
对于具体业务来讲,不变的是必定要有一个Call网络工做对象,因此Retrofit能够有一个生产对象的机制(像工厂同样)
具体业务的易变性
对于具体业务来讲,易变的就是这个Call网络工做对象的类型,不只有CallBacl回调、可能还有Flowable工做流、或者其余潜在的对象类型。
针对这种Call对象的易变性,Retrofit也是没法提供一个万能的实现类,因此也是提供了扩展解耦,容许开发者本身定义CallAdapterFactory和CallAdapter,去实现潜在的Call类型转换。
由于这种Call对象的生产须要有大量的配套代码,为了简化代码,Retrofit使用动态代理来生产这个对象。
最后,由于须要处理的方法和对象太多太复杂,须要使用建造者模式来把建造过程和使用过程分离开。
这样倒着走一遍以后,咱们再看Retrofit的设计和实现原理,就会以为水到渠成,对于Retrofit精妙的设计更会有一种切身体会。
在上文的反推过程当中,咱们可窥见(瞎猜)Jake大神的一些思路:
最后感叹一下。
对于网络访问的抽象与优化,其实是个很是难的课题,在Retrofit以前,你们努力的方向基本上都是Volley/OkHttp这种围绕底层网络访问的工做。
由于越底层的东西越容易抽象,越上升到接近业务层,就越容易在纷扰的业务层中迷失。
Retrofit能精准地抓到Call网络工做对象这个关键点,并能经过一系列精巧的设计实现对这种类型“飘忽不定”的对象的自动化定制生产,着实使人赞叹。
Retrofit
你真的会用Retrofit2吗?Retrofit2彻底教程
Retrofit2 源码解析
Retrofit 框架源码学习
拆轮子系列:拆 Retrofit
Android 动态代理以及利用动态代理实现 ServiceHook
--------------------
3.Retrofit 框架源码学习
Retrofit 是 Square 安卓平台网络层三板斧最后一个项目,Retrofit 依赖 OkHttp 。Retrofit 让 http 网络请求更加清晰。
声明一个接口,并用接口描述 request
public interface GitHubService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Path("user") String user); }
方法上面的注释表示 request 的接口名 ,方法的返回类型就是 http 请求的返回值,方法的参数就是 http 的请求参数。
建立一个 Retrofit 客户端
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build(); GitHubService service = retrofit.create(GitHubService.class);
Retrofit 建立的时候指定了 request 的接口地址,而后调用 retrofit.create 方法建立一个 GitHubService 实例。
发起网络请求
Call<List<Repo>> repos = service.listRepos("octocat"); repos.execute().body()
上面的例子能够看到,retrofit.create() 方法会建立一个 GitHubService 实例,可是 GitHubService 自己是一个接口。
为了了解 retrofit.create() 方法,咱们先看下 Retrofit 的建立过程。
Retrofit 和 OkHttp 同样都是使用构建者模式建立对象。先看下 Retrofit.Builder 的 build() 方法。
public Retrofit build() { if (baseUrl == null) { throw new IllegalStateException("Base URL required."); } okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { callFactory = new OkHttpClient(); } Executor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); } // Make a defensive copy of the adapters and add the default Call adapter. List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); // Make a defensive copy of the converters. List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories); return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); }
建立 Retrofit 的时候须要传递一下几个参数
1. callFactory 用来建立一个实现了 okhttp3.Call.Factory 的对象,若是没有设置,默认为 OkHttpClient。 2. baseUrl 网络接口的地址。 3. converterFactories 用来把服务器返回的数据转换为对象。 4. adapterFactories 用来发起网络请求。 5. callbackExecutor 是一个调度器,用来接收返回的数据,在 Android 上默认是封装了 handler 的 MainThreadExecutor 6. validateEagerly 是一个开关,若是为 true 会缓存建立的 ServiceMethod 。
public <T> T create(final Class<T> service) { Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service}, new InvocationHandler() { private final Platform platform = Platform.get();
这里用到了一个公共技术点之 Java 动态代理,create 方法传入一个 Class ,这个 Class 对象就是上文的 GitHubService 的 Class 。
GitHubService 的方法是由 InvocationHandler 代理实现的,重点看三行代码
……
ServiceMethod<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall);
ServiceMethod<?, ?> loadServiceMethod(Method method) { ServiceMethod<?, ?> result = serviceMethodCache.get(method); if (result != null) return result; synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { result = new ServiceMethod.Builder<>(this, method).build(); serviceMethodCache.put(method, result); } } return result; }
这里建立了一个 ServiceMethod 对象。
OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) { this.serviceMethod = serviceMethod; this.args = args; }
建立了一个 OkHttpCall ,serviceMethod 和 args 是 OkHttpCall 的成员函数。
因此,
这里须要明白 serviceMethod.callAdapter 是怎么来的
1. 在 ServiceMethod.Builder.build() 中调用 createCallAdapter() 2. 在 createCallAdapter() 中会找到 (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations) 3. 在 callAdapter() 中调用 nextCallAdapter 4. nextCallAdapter 会遍历 adapterFactories 返回一个 CallAdapter。
这里再回头看下 adapterFactories Retrofit.Builder.build() 方法中
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
而在 Retrofit.nextCallAdapter() 中
int start = adapterFactories.indexOf(skipPast) + 1; for (int i = start, count = adapterFactories.size(); i < count; i++) { CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this); if (adapter != null) { return adapter; } }
若是没有设置 AdapterFactory 将会使用一个默认的 AdapterFactory
CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) { if (callbackExecutor != null) { return new ExecutorCallAdapterFactory(callbackExecutor); } return DefaultCallAdapterFactory.INSTANCE; }
因此若是咱们设置了一个 RxJavaCallAdapterFactory,就会返回 RxJavaCallAdapterFactory。
经过 retrofit.create() 咱们能够知道,retrofit.create() 返回的是一个代理对象InvocationHandler ,那么在执行
Call<List<Repo>> repos = service.listRepos("octocat");
方法时,调用的其实是 callAdapter.adapt(okHttpCall),以 DefaultCallAdapterFactory 为例
结合 retrofit.create() 方法能够得知这里返回的是一个 OkHttpCall 对象。
接下来使用 OkHttpCall.execute() 或者 异步执行 enqueue(Callback<T> callback)
这两种方式都会调用 createRawCall() 建立一个 okhttp3.Call
private okhttp3.Call createRawCall() throws IOException { Request request = serviceMethod.toRequest(args); okhttp3.Call call = serviceMethod.callFactory.newCall(request); if (call == null) { throw new NullPointerException("Call.Factory returned null."); } return call; }
此处的 serviceMethod.callFactory 就是 retrofit.create() 中建立的 OkHttpClient()
后面的内容都是由 Okhttp 模块接管,进行网络请求,参考okHttp 框架源码学习
而后调用 parseResponse(call.execute())
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.body(); …… 省略一些 http 返回值处理逻辑 …… try { T body = serviceMethod.toResponse(catchingBody); return Response.success(body, rawResponse); } …… }
okHttp 请求网络的返回数据,会交给 serviceMethod.toResponse
R toResponse(ResponseBody body) throws IOException { return responseConverter.convert(body); }
在 ServiceMethod.Builder.build() 方法中能够找到 responseConverter 是经过 createResponseConverter() 方法的返回对象。
createResponseConverter() 只是报包裹了 retrofit.responseBodyConverter(responseType, annotations) 方法。
retrofit.responseBodyConverter() 继续跟踪下去会得知,返回的是 converterFactories 数组的第 0 个对象,也就是内置的 BuiltInConverters.responseBodyConverter() 方法返回的 BufferingResponseBodyConverter
static final class BufferingResponseBodyConverter implements Converter<ResponseBody, ResponseBody> { static final BufferingResponseBodyConverter INSTANCE = new BufferingResponseBodyConverter(); @Override public ResponseBody convert(ResponseBody value) throws IOException { try { // Buffer the entire body to avoid future I/O. return Utils.buffer(value); } finally { value.close(); } } }
再看一下 Utils.buffer(value)
static ResponseBody buffer(final ResponseBody body) throws IOException { Buffer buffer = new Buffer(); body.source().readAll(buffer); return ResponseBody.create(body.contentType(), body.contentLength(), buffer); }
最终会返回一个从新封装的 Okhttp 框架的 ResponseBody 对象。
2.Retrofit分析-经典设计模式案例
retrofit就是对okhttp再作了一层封装。
本篇是retrofit番外篇。只讲retrofit中的设计模式以及我我的的理解与延伸。若是你还没看过retrofit源码,不妨先看看这篇Retrofit分析-漂亮的解耦套路。
之前用的volley, async-http-lib, xUtils。这些libs基本都是上图这个workflow。向服务器请求API总共分三步。
现在的retrofit也是换汤不换药的。也是这三步。
CallAdapter
(你能够把它理解成executor)Converter
(解析数据并转换成T)原本还应该有个CallFactory
来切换具体的http client的。就像volley那样>=9使用HttpUrlConnection
, <9使用HttpClient
。
不过我想都是square出品,彼此互推也是理所固然的啊。我相信之后okhttp确定是惟一的请求client。
上面说CallAdapter
能够理解成executor,具体是什么,咱们下面讲具体设计模式时再详细讨论。
Retrofit给咱们暴露的方法和类很少。核心类就是Retrofit,咱们只管配置Retrofit,而后作请求。剩下的事情就跟上层无关了,只须要等待回调。这样大大下降了系统的耦合度。对于这种写法,咱们叫外观模式(门面模式)。
几乎全部优秀的开源library都有一个门面。好比Glide.with()
ImageLoader.load()
Alamofire.request()
。有个门面方便记忆,学习成本低,利于推广品牌。 Retrofit的门面就是retrofit.create()
当咱们本身写的代码的时候尽可能也要这样来作。
好比咱们有一个独立并公用的模块,须要供其余模块来调用。好比download
,location
,socialshare
等
最好咱们写一个module
,将全部相关的代码都放在这个module
中。这是第一步。
第二步,为你的module
提供一个漂亮的门面。好比下载的DownloadManager
, 经纬度的LocationTracker
, 社交分享的SocialManager
。它们作为功能模块的入口,要尽可能的简洁,方法命名好记易理解,类上要有完整的示例注释
第三步,闭门造车。无论你在里面干什么,外面都是不知道的,就像薛定谔的那只猫,外层不调用它,永远不知道它是否好用。
不过为了之后好维护,不给他人留坑,仍是尽可能写的工整一些。
装饰模式跟静态代理很像。
每次一说装饰模式,就想成decorator,实际上叫wrapper更直观些。既然是wrapper,那就得有源的句柄,在构造wrapper时得把source做为参数传进来。wrapper了source,一样还wrapper其余功能。
代理模式,Proxy Delegate,实际上Delegate也不知道本身被代理了,Proxy假装成Delegate来执行,既然是proxy,那proxy不该该提供delegate没有的public方法,以避免被认出来。
抛开理论的描述,咱们直接来看下面的代码。
你能够将ExecutorCallbackCall
看成是Wrapper,而真正去执行请求的源Source是OkHttpCall
。之因此要有个Wrapper类,是但愿在源Source操做时去作一些额外操做。这里的操做就是线程转换,将子线程切换到主线程上去。
简单的解释下,enqueue()方法是异步的,也就是说,当你调用
OkHttpCall
的enqueue方法,回调的callback是在子线程中的,若是你但愿在主线程接受回调,那须要经过Handler转换到主线程上去。ExecutorCallbackCall
就是用来干这个事。固然以上是原生retrofit使用的切换线程方式。若是你用rxjava,那就不会用到这个ExecutorCallbackCall
而是RxJava
的Call了。这里不展开。
再来讲动态代理。以往的动态代理和静态代理使用的场景是相似的。都想在delegate调用方法先后作一些操做。若是个人代理类有不少方法,那我得额外写不少代码,因此这时候就引入了动态代理。经过动态设置delegate,能够处理不一样代理的不一样方法。看不懂不要紧,直接上代码:
简而言之,动态代理就是拦截调用的那个方法,在方法先后来作一些操做。Retrofit里的动态代理比较巧妙。实际上它根本就没有delegate。由于这个方法没有真正的实现。使用动态代理,只是单纯的为了拿到这个method上全部的注解。
全部的工做都是由proxy作了。比起咱们总说代理就是打log要高明多了。
我之前本身写数据库框架时,也碰到这样的场景。一个类里有不少一对一,一对多关系。若是从db里fetch出来都去作初始化,那会很是影响性能。但若是不初始化,到使用时再去手动初始化就更麻烦了。怎么办呢?
class A{ private B b; private ArrayList<C> cs; public B getB(){ return b; } public ArrayList<C> getCs(){ return cs; } }
当类A里的get方法被invoke时,我就判断,这个类有没有被初始化,若是有,那就不作任何操做。
若是没有,那得等会,我把数据从数据库中fetch出来给你赋值后,再去invoke。这个场景能够叫懒加载,能够套用AOP面向切面编程。
动态代理能实现这个需求吗?能够,可是支持的很糟糕。由于动态代理依赖接口实现,总不能将全部的pojo中的方法都申明到接口里吧?那真是要命了。
因此我用了种替代方案,既然是AOP,有个面向切面的框架AspectJ
。你能够经过它来切入这些get方法,先判断有没初始化,而后再返回。
差很少就是这样,没有Proxy的概念,只是在编译时,把这些切面织入进去。对于pojo而言彻底是透明的。是否是很6。
不过这里也有不少其余的性能瓶颈,好比说我在第一次调用时,要先去数据库fetch,这也是耗时操做。这个先跳过,有机会再跟你们八一八,我那数据库框架是怎么撸出来的。
适配器模式就是,已经存在的OkHttpCall
,要被不一样的标准,平台来调用。 设计了一个接口CallAdapter
,让其余平台都是作不一样的实现来转换,这样不花很大的代价就能再兼容一个平台。666。
若是你已经看过retrofit源码,极可能被CallAdapter
玩坏。这个CallAdapter
不是那么好理解。先抛开代码,咱们来看看适配器模式。
Adapter简单来讲,就是将一个已存在的东西转换成适合咱们使用的东西。就比方说电源Adapter。出国旅游都要带转接头。比方说,RecyclerView
里的Adapter是这么定义的。Adapters provide a binding from an app-specific data set to views。
再回来看看Retrofit,为何咱们须要转接头呢。那个被转换的是谁?
咱们看看CallAdapter
的定义。Adapts a {@link Call} into the type of {@code T}.
这个Call是OkHttpCall,它不能被咱们直接使用吗?被转换后要去实现什么特殊的功能吗?
咱们假设下。一开始,retrofit只打算在android上使用,那就经过静态代理ExecutorCallbackCall
来切换线程。可是后来发现rxjava挺好用啊,这样就不须要Handler来切换线程了嘛。想要实现,那得转换一下。将OkHttpCall
转换成rxjava(Scheduler
)的写法。再后来又支持了java8(CompletableFuture
)甚至竟然还有iOS支持。大概就是这样一个套路。固然我相信square的大神确定一开始就考虑了这种状况,从而设计了CallAdapter
。
在retrofit里,这个适配器模式不是那么明显。并且和其余模式交错在一块儿,因此看起来很麻烦。好比这个CallAdapter又夹杂着策略模式(仅是我的见解)。
你能够看看Rxjava里如何去建立adapter的,它是根据api方法声明的returnType来建立具体的CallAdapter
实例的。上代码你就明白了。
是否是很像根据不一样的策略使用不一样的算法?不一样的returnType声明就是set不一样的Strategy。
来张提纲挈领的流程图,没保存的赶忙存起来。之后就能照着它本身开车了。
好,大概就将这么多啦。这些就是retrofit的解耦套路了。经过一系列的设计模式,封装思想来解耦,
看到如今,其实retrofit就是一个负责调度的controller。先给retrofit配置好,让它可以正常工做。你给它一个方法调用,它就在内部开始运转。这个方法之前我消化过吗,没消化那就用一个ServiceMethod来解析它。解析后要用来配置一个request请求。但它本身搞不定这事啊,因此须要给它一个转接头,经过转接头来使用okhttpcall。请求是作好了,可是response它又不认识,
因此又请来convertor来帮忙,转换完毕以后才吐出一个咱们最终要的那个对象。
若是看文章不够过瘾,能够看Stay精心录制的视频Retrofit分析-漂亮的解耦套路,看完你不再怕看不懂retrofit了。
并且你还能够用Stay这种分析套路来轻松看懂其余源码。
1.Retrofit分析-漂亮的解耦套路
万万没想到Retrofit会这么火,在没看源码以前,我简单的认为是由于它跟OkHttp同出一源(Square),因此才会炒的那么热。又或者是由于它能支持RxJava,因此火上浇油,一发不可收拾。
后来看过Retrofit源码以后,我才理解为何它倍受关注,是由于它集诸优势于一身,而且炒鸡解耦。你能预见的特殊需求,都能很是容易的扩展。
咱们先来看一下没有HTTP框架之前,咱们是如何作请求的。
大概是以上4大步骤,在没有框架的年代,想要作一次请求,是万分痛苦的,你须要本身管理线程切换,须要本身解析读取数据,解析数据成对象,切换回主线程,回调给上层。
这段空白的时间持续了好久。从我10年工做起到12年,由于写烦了重复的代码,因此就得想办法,把那些变化的地方封装起来,也只是简单的封装。
好在官方出了AsyncTask,虽然坑不少,但若是再本身维护一个队列,基本不会出现问题。
更好的地方是数据格式从xml变成json了。gson解放了双手,不再用解析dom了。
后来慢慢出了很多真正的HTTP框架。Stay也借鉴了不少文章,封装了一套适用于自身业务需求的框架。
这个时期的框架有个特色,就是拼了命去支持全部类型。比方说Volley支持直接返回Bitmap。xUtils不只大而全,并且连多线程下载也要支持。
在资源匮乏的时代,它们的存在有它们的道理。但若是说如今还用Volley作图片请求,还在用xUtils或Afinal里的各个模块。那就说不过去了。
术业有专攻,百家争鸣的时期,难道不应选择最好的那一个吗?(Stay没真的用过xUtils和Afinal这种组合框架,潜意识告诉我,它们有毒,一旦某个环节出问题或者须要扩展,那代价就太大了)
tips:本文以retrofit最新版本2.0.1为例,你们也能够去github下源码,找tag为'parent-2.0.1'就能够。目前代码变更比较大。2.0.1已经使用okhttp3了,而我项目中2.0.0-beta2仍是okhttp2.5。
retrofit的最大特色就是解耦,要解耦就须要大量的设计模式,
先来看一张Stay画的精简流程图(若有错误,请斧正),类图就不画了。
Stay在一些设计模式很明确的地方作了标记。
外观模式,动态代理,策略模式,观察者模式。固然还有Builder模式,工厂等这些简单的我就没标。
先简述下流程吧:
经过门面Retrofit来build一个Service Interface的proxy
当你调用这个Service Interface中的某个请求方法,会被proxy拦截。
经过ServiceMethod来解析invoke的那个方法 ,经过解析注解,传参,将它们封装成咱们所熟悉的request。而后经过具体的返回值类型,让以前配置的工厂生成具体的CallAdapter,ResponseConverter,这俩咱们稍后再解释。
new一个OkHttpCall,这个OkHttpCall算是OkHttp的包装类,用它跟OkHttp对接,全部OkHttp须要的参数均可以看这个类。固然也仍是能够扩展一个新的Call的,好比HttpUrlConnectionCall。可是有点耦合。看下图标注:
红框中显式的指明了OkHttpCall,而不是经过工厂来生成Call。因此若是你不想改源码,从新编译,那你就只能使用OkHttp了。不过这不碍事。(可能也是由于还在持续更新中,因此这块可能后面会改进的)
生成的CallAdapter有四个工厂,分别对应不一样的平台,RxJava, Java8, Guava还有一个Retrofit默认的。这个CallAdapter不太好用中文解释。 简单来讲就是用来将Call转成T的一个策略。由于这里具体请求是耗时操做,因此你须要CallAdapter去管理线程。怎么管理,继续往下看。
好比RxJava会根据调用方法的返回值,如Response<'T> |Result<'T>|Observable<'T> ,生成不一样的CallAdapter。实际上就是对RxJava的回调方式作封装。好比将response再拆解为success和error等。(这块仍是须要在了解RxJava的基础上去理解,之后有时间能够再详细作分析)
在步骤5中,咱们说CallAdapter还管理线程。比方说RxJava,咱们知道,它最大的优势能够指定方法在什么线程下执行。如图
在adapt Call中,具体的调用了Call execute(),execute()是同步的,enqueue()是异步的。由于RxJava已经切换了线程,因此这里用同步方法execute()。
接下来的具体请求,就是OkHttp的事情了,retrofit要作成的就是等待返回值。在步骤4中,咱们说OkHttpCall是OkHttp的包装类,因此将OkHttp的response转换成咱们要的T,也是在OkHttpCall中执行的。
固然具体的解析转换操做也不是OkHttpCall来作的,由于它也不知道数据格式是什么样的。因此它只是将response包装成retrofit标准下的response。
Converter->ResponseConverter,很明显,它是数据转换器。它将response转换成咱们具体想要的T。Retrofit提供了不少converter factory。好比Gson,Jackson,xml,protobuff等等。你须要什么,就配置什么工厂。在Service方法上声明泛型具体类型就能够了。
最后,经过声明的observeOn线程回调给上层。这样上层就拿到了最终结果。至于结果再如何处理,那就是上层的事了。
再来回顾下Stay画的流程图:
这真是漫长的旅行,Stay也是debug一个个单步调试才梳理出来的流程。固然其中还有不少巧妙的解耦方式,我这里就不赘述了。你们能够看看源码分析下,当真是设计模式的经典示例。
我想如今你们应该对retrofit有所了解了。当你再给别人介绍retrofit的时候,就别只说它的注解方式多新颖,多炫技了。注解式框架有不少的,像j2ee中一大把。因此注解算不得多精湛的技艺。 最牛逼的仍是它的解耦方式,这个套路没有多年的实际架构经验是设计不出来的。