文章目录
前言
核心思想就是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->OkHttpCallokhttp3.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方法中,统一对我们接口中的所有方法进行解析,通过注解和反射的方法解析出来,将参数封装到










