前言
核心思想就是AOP思想,面向切面编程。
AOP使用场景比如:LeakCanary、BlockCanary、Matrix、LifeCycle、OkHttp(拦截器)等
1.如何封装OkHttp
OkHttp是一个网络请求框架,不过这个网络请求框架存在一些不足:
- 用户网络请求的接口配置繁琐,尤其是需要配置复杂的请求body、请求头和请求参数的时候;
- 数据的解析过程需要用户手动拿到ResponseBody进行解析,不能复用;
- 无法适配自动进行线程的切换;
- 嵌套网络请求数据回调存在问题。
我们可以通过Retrofit来对OkHttp网络请求框架进行改造,具体的改造如下:
- 请求前
- 统一配置网络请求头
- 一致适配请求Request
- 返回结果
- 数据适配
- 线程切换
2.Retrofit的设计思想
Retrofit的使用方式:
//构建出Retrofit对象
var retrofit = Retrofit.Builder()
.baseUrl("https://www.wanandroid.com/")//请求地址域名
.addConverterFactory(GsonConverterFactory.create(Gson()))//返回数据进行Gson解析
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//通过RxJava实现线程间切换
.build()
//基于访问接口创建一个接口类型对象,通过动态代理生成了一个实现类
val retrofitService = retrofit.create<IRetrofitService>(IRetrofitService::class)
//基于接口类型对象构建以及参数,构建一个OkHttp的Call接口实现类ExecutorCallbackCall
var call = retrofitService.banner()
//将请求Call添加到OkHttp请求队列,最终执行的是Call接口的实现类ExecutorCallbackCall.enqueue方法
//当在调用ExecutorCallbackCall.enqueue方法时,会调用delegate.enqueue方法,
//而这个delegate是一个OkHttpCall对象,所以最终执行的是OkHttpCall.enqueue方法
call.enqueue(object:retrofit2.Callback<BannerBean>{
override fun onFailure(call: retrofit2.Call<BannerBean>, exception: Throwable) {
}
override fun onResponse(call: retrofit2.Call<BannerBean>, response: retrofit2.Response<BannerBean>) {
Log.e("TangKunLog",response.body().toString())
}
})
3.ServiceMethod存在的价值
Retrofit的create()方法中,InvocationHandler接口回调的invoke方法,每回调一次invoke方法,就表示执行了接口中的一个方法,invoke方法的返回值是通过如下代码:
retrun loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
这个方法包含的业务逻辑,先总结在这里,有一个整体的认识,然后通过后面的知识点去论证这个观点。
- loadServiceMethod(method)
- 该方法最终会返回
ServiceMethod
接口的实现类HttpServiceMethod
; HttpServiceMethod
类在解析注解的时候会调用createCallAdapter()
创建CallAdapter
接口,并重写适配方法adapt
返回接口Call
的实现类ExecutorCallbackCall
。
那么ExecutorCallbackCall
是怎么来的呢?
在通过Builder建造者模式创建Retrofit对象时,调用build()方法中,会通过如下代码创建默认的请求适配器工厂类DefaultCallAdapterFactory
,
代码如下:
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
->new DefaultCallAdapterFactory(callbackExecutor);
上面的ExecutorCallbackCall
就是通过DefaultCallAdapterFactory
类中的get()
方法创建得来,这里流程相对比较复杂,知道结果即可。- 参数
method
就是我们接口类(如:IRetrofitService.java
)中的一个方法(如:banner
)
- 该方法最终会返回
- invoke(args != null ? args : emptyArgs)
- 通过
HttpServiceMethod.invoke()
最终创建Call
接口的实现类OkHttpCall
- 参数
args
就是接口中的每一个方法包含的参数数组
- 通过
loadServiceMethod(method)
这个方法会返回ServiceMethod
抽象类的实现类HttpServiceMethod
,然后就会通过如下代码去解析接口请求方法所包含的注解信息:
ServiceMethod.parseAnnotations(this, method);
注解解析完成后,然后将注解信息添加到接口请求工厂中去RequestFactory
。 RequestFactory.java
代码如下:
RequestFactory(Builder builder) {
method = builder.method;//方法
baseUrl = builder.retrofit.baseUrl;//域名
httpMethod = builder.httpMethod;//get/post请求方式
relativeUrl = builder.relativeUrl;//接口地址
headers = builder.headers;//请求头
contentType = builder.contentType;//数据类型
hasBody = builder.hasBody;//是否包含body
isFormEncoded = builder.isFormEncoded;
isMultipart = builder.isMultipart;//是否是多个参数
parameterHandlers = builder.parameterHandlers;
isKotlinSuspendFunction = builder.isKotlinSuspendFunction;
}
由于前面loadServiceMethod(method)
方法会返回ServiceMethod
抽象类的实现类HttpServiceMethod
,接着调用invoke
方法的时候,其实就是调用的HttpServiceMethod.invoke()
方法,而这个方法会创建OkHttpCall
对象,作为方法的返回值中的参数。
HttpServiceMethod.java
中的源码如下:
@Override
final @Nullable ReturnT invoke(Object[] args) {
//创建Call接口的实现类OkHttpCall对象,作为抽象方法adapt的参数返回
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
//抽象方法,由于call参数是上面创建的OkHttpCall<ResponseT>对象。
//因此,OkHttpCall在调用enqueue方法发起异步请求后,回调的方法中的泛型对象(比如:BannerBean)就是上面invoke方法的返回值,同样也是我们接口请求的返回值
protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);
综上所述,我们通过Retrofit.create()方法构建的接口,调用接口中每一个方法发起网络请求,最终会返回的Call接口的实现类OkHttpCall,所以调用的其实是OkHttpCall的同步或异步请求,然后在CallBack中回调onResponse和onFailure方法,返回响应的结果。
4.Retrofit的整理流程
- 通过
Builder
构建了一个Retrofit
对象,可以设置如下功能:baseUrl
、对响应数据进行Gson
解析、通过RxJava
实现线程切换使得网络请求的回调在主线程中,等等。 - 通过
Retrofit.create(IRetrofitService.class)
方法创建接口的动态代理类对象 - 调用接口中的方法,就会执行动态代理中的
invoke
方法,创建ServiceMethod
对象,用于封装请求的参数;同时构建一个ExecutorCallbackCall
对象,作为接口调用方法返回Call
接口的实现类 - 调用
Call.enquque
方法发起网络请求,就会调用到实现类ExecutorCallbackCall
的同步或者是异步请求方法,此时就会通过适配器模式将ExecutorCallbackCall
适配出一个OkHttpCall
对象,同时会将前面封装的ServiceMethod
中的参数封装到OkHttp的Request
对象中去,然后通过工厂.newCall(Request)
方法构造出OkHttp3.Call
对象,最终调用OkHttp
的Call
对象的同步或异步请求方法,完成整个网络请求。 - 总结:
也就是说Retrofit
只是对数据进行了封装,将一些请求参数封装到ServiceMethod
对象,封装出Call
接口的实现类ExecutorCallbackCall
对象;最终还是会把这些数据封装到OkHttp
层,将ServiceMethod
封装到Request
对象中去,将ExecutorCallbackCall
封装到OkHttp3.Call
对象中去,让OkHttp
去实现网络请求。
5.Retrofit使用到了哪些设计模式
-
建造者设计模式
Builder
- 通过Builder模式创建出我们的Retrofit对象,可以对配置的参数进行自由选择。
-
外观/门面模式
- Retrofit通过对内部参数的封装,可以直接通过Retrofit类与外界进行交互,我们可以不用关心内部实现的复杂细节。
- 举例:
我们需要通过Glide展示图片,可以将Glide封装到一个工具类ImageUtils类中;我们的想用在展示图片的时候直接调用ImageUtils中的方法即可,当有一个更好的网络框架出现的时候,我们只需要对ImageUtils中的加载图片的代码进行替换即可,而不需要在每一个使用到加载图片的地方都去做更改,大大提高了我们的开发效率。
-
动态代理
- 利用了AOP思想,在调用Retrofit.create()方法时会创建出网络接口实现类,实现类在调用实现的接口中的每一个方法时,就会调用invocationHandler.invoke()方法,这些方法最终都会统一调用到Retrofit.create()方法的源码中的InvocaionHandler接口实现的invoke方法。
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]; //参数1,接口类的代理类对象,(代理类可以存在多个,是通过上面数组中service传递进来的,代码:new Class<?>[] { service }) //参数2,代理类中实现的网络请求接口中的方法 //参数3,代理类中每一个方法中的参数 @Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { //如果该方法是Object中的方法,则遵循正常调用 if (method.getDeclaringClass() == Object.class) { //代理类对象调用方法 return method.invoke(this, args); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } //将方法中的注解解析出来,构建出请求网络的参数对象ServiceMethod,然后调用这个方法的参数,最终返回一个Call接口的实现类ExecutorCallbackCall return loadServiceMethod(method).invoke(args != null ? args : emptyArgs); } }); }
- 利用了AOP思想,在调用Retrofit.create()方法时会创建出网络接口实现类,实现类在调用实现的接口中的每一个方法时,就会调用invocationHandler.invoke()方法,这些方法最终都会统一调用到Retrofit.create()方法的源码中的InvocaionHandler接口实现的invoke方法。
-
装饰器模式
- 使用场景:
okhttp3.Call->ExecutorCallbackCall->OkHttpCall
okhttp3.Call
作为一个接口OkHttpCall
对请求的参数做了一系列的封装ExecutorCallbackCall
是对OkHttpCall
的封装,将我们的请求的回调CallBack
通过Handler
放在主线程中执行,所以我们使用call.enqueue(object:Callback<BannerBean>{}
,onResponse
和onFailure
都是执行在主线程中。
- 装饰器在Android中其他使用场景:
-
Context
- ContextImpl
- ContextWrapper
- ContextThemeWrapper
- Activity
- Service
- Application
- ContextThemeWrapper
-
new File(file)
- new FileOutputStream(new File(file))
- new BufferedOutputStream(new FileOutputStream(new File(file)))
- DataOutputStream dataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(new File(file))))
- new BufferedOutputStream(new FileOutputStream(new File(file)))
- new FileOutputStream(new File(file))
-
- 使用场景:
-
代理模式
- 我们在使用
Retrofit.create(IRetrofitService.class)
方法时,这个create
方法的源码中就会调用如下代码,采用动态代理的方式,创建出这个接口的代理类对象,代码如下:
Proxy.newProxyInstance(service.getClassLoader,new Class<?>[]{service},new InvocationHandler(){...invoke()...})
通过这个代理类对象,就可以调用接口中的方法,从而实现网络请求。
- 我们在使用
-
适配器模式
- 使用场景:
OkHttpCall->ExecutorCallbackCall
- 在
DefaultCallAdapterFactory
类的get()
方法中,然后在返回的CallAdapter
接口重写的adapt(Call<Object> call)
方法中,将OkHttpCall
对象适配成为ExecutorCallbackCall
对象。
DefaultCallAdapterFactory.java
中代码如下:@Override public @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { //省略非核心代码 return new CallAdapter<Object, Call<?>>() { @Override public Call<Object> adapt(Call<Object> call) { return executor == null ? call //将传入的OkHttpCall对象通过适配成为ExecutorCallbackCall对象 : new ExecutorCallbackCall<>(executor, call); } }; }
- 使用场景2:
在接口类中的每一个方法,可以返回Call
对象,同时也可以返回Observable
对象;从Call
对象转换成Obervable
对象的过程中,就采用了适配器模式来实现。
RxJava2CallAdapter.java
代码如下:public Object adapt(Call<R> call) { //根据同步请求还是异步请求,通过适配器将Call转换成Observable返回 Observable<Response<R>> responseObservable = isAsync ? new CallEnqueueObservable<>(call) : new CallExecuteObservable<>(call); }
- 其他使用场景:
Gson解析,将String字符串通过适配器转换成JavaBean对象,同理,也可以将JavaBean通过适配器转换成String字符串。
- 在
- 使用场景:
如何使用设计模式:
- 什么是设计模式?
- 分析框架源码中采用了哪些设计模式?为什么要使用设计模式?有什么好处?
- 思考自己的项目,可以使用哪些设计模式?能带来什么好处?
面试题
Retrofit使用过程中的几个疑惑:
- Interface接口能够直接用?
- 因为使用了动态代理生成了一个实现类,使用的是实现类的对象。
- 参数给了谁?
- 接口请求方法中的参数会通过注解和反射解析出来,然后封装成
ServiceMethod
对象,最终到OkHttp
网络请求的Request
对象中去。
- 接口请求方法中的参数会通过注解和反射解析出来,然后封装成
- 具体请求的url是怎么形成的?
- 通过
Retrofit
在通过Builder
的时候传入的baseUrl
,和我们的接口请求方法上通过注解传入的接口请求地址relativeUrl
,会被解析到ServiceMethod
对象中去,baseUrl
和relativeUrl
共同构建成OkHttp
网络请求Request
中的url
参数。
- 通过
- 为什么所有的请求都是同一个方式?
- 采用AOP思想,在动态代理创建接口实现类的InvocationHandler的invoke方法中,统一对我们接口中的所有方法进行解析,通过注解和反射的方法解析出来,将参数封装到
OkHttp
的Request
对象,同时封装出OkHttp
的Call
对象,最终通过OkHttp完成同步或异步网络请求。
- 采用AOP思想,在动态代理创建接口实现类的InvocationHandler的invoke方法中,统一对我们接口中的所有方法进行解析,通过注解和反射的方法解析出来,将参数封装到