最新Android开发:从设计者角度看Retrofit原理,在线面试技巧

最后是今天给大家分享的一些独家干货:

【Android开发核心知识点笔记】

【Android思维脑图(技能树)】

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

【Android高级架构视频学习资源】

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

//上报

post()

}

//业务层方法test2

fun test2(){

//数据库更新操作

dao.update()

//上报

post()

}

以上这种方式存在一个问题:

上报操作本身与具体业务无关,一旦需要对上报进行修改,那就可能影响到业务,进而可能造成不可预期的问题产生

面对以上问题可以通过代理模式完美规避,改造后的代码如下:

class DaoProxy(){

//数据库插入操作

fun insert(){

dao.insert()

//上报

post()

}

//数据库更新操作

fun update(){

dao.update()

//上报

post()

}

}

//业务层方法test1

fun test1{

//数据库插入操作

daoProxy.insert()

}

//业务层方法test2

fun test2(){

//数据库更新操作

daoProxy.update()

}

新增一个代理类DaoProxy,将dao以及上报操作在代理类中执行,业务层直接操作代理对象,这样就将上报从业务层抽离出来,从而避免业务层改动带来的问题。实际使用代理模式时应遵守基于接口而非实现编程思想,但文章侧重于传授思想,规范上可能欠缺

此时还有一个问题,每次CRUD都会手动做一次上报操作,这显然是模版代码,如何解决?下面来看动态代理:

什么是动态代理?

java中的动态代理就是在运行时通过反射为目标对象做一些附加操作,代码如下:

class DaoProxy() {

//创建代理类

fun createProxy(): Any {

//创建dao

val proxyAny = Dao()

val interfaces = proxyAny.javaClass.interfaces

val handler = ProxyHandler(proxyAny)

return Proxy.newProxyInstance(proxyAny::class.java.classLoader, interfaces, handler)

}

//代理委托类

class ProxyHandler(private val proxyObject:Any): InvocationHandler {

//代理方法,p1为目标类方法、p2为目标类参数。调用proxyObject任一方法时都会执行invoke

override fun invoke(p0: Any, p1: Method, p2: Array): Any {

//执行Dao各个方法(CRUD)

val result = p1.invoke(proxyObject,p2)

//上报

post()

return result

}

}

}

//此处规范上应该使用基于接口而非实现编程。如果要替换Dao通过接口编程可提高扩展性

val dao:Dao = DaoProxy().createProxy() as Dao

dao.insert()

dao.update()

其中Proxy是JDK中用于创建动态代理的类,InvocationHandler是一个委托类, 内部的invoke(代理方法)方法会随着目标类(Dao)任一方法的调用而调用,所以在其内部实现上报操作即可消除大量模版代码。

动态代理与静态代理核心思想一致,区别是动态代理可以在运行时通过反射动态创建一个切面(InvocationHandler#invoke),用来消除模板代码。喜欢思考的同学其实已经发现,代理模式符合面向切面编程(AOP)思想,而代理类就是切面

3.3 动态代理获取ApiService

================================================================================

2.2小节有提到可以通过retrofit.create()创建ApiService,跟一下retrofit的create()

#Retrofit.class

public  T create(final Class service) {

//第一处

validateServiceInterface(service);

return (T) Proxy.newProxyInstance(

service.getClassLoader(),

new Class<?>[] {service},

new InvocationHandler() {

//第二处

@Override

public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)

throws Throwable {

return platform.isDefaultMethod(method)

? platform.invokeDefaultMethod(method, service, proxy, args)

: loadServiceMethod(method).invoke(args);

}

});

}

create()大致可以分为两部分:

  • 第一部分为validateServiceInterface()内容,用来验证ApiService合法性,比较简单就不多描述,感兴趣的同学可自行查看。

  • 第二部分就是invoke(),通过3.2小节可知这是一个代理方法,可通过调用ApiService中的任一方法执行,其中参数method和args代表ApiService对应的方法和参数。返回值中有一个isDefaultMethod,这里如果是Java8的默认方法直接执行,毕竟我们只需要代理ApiService中方法即可。经过反复筛选最后重任落在了loadServiceMethod,这也是Retrofit中最核心的一个方法,下面我们来跟一下

#Retrofit.class

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;

}

大致就是对ServiceMethod做一个很常见的缓存操作,这样做的目的是为了提升运行效率,毕竟创建一个ServiceMethod会用到大量反射。创建ServiceMethod对象是通过其静态方法parseAnnotations实现的,再跟一下这个方法:

#ServiceMethod.class

static  ServiceMethod parseAnnotations(Retrofit retrofit, Method method) {

//第一步

RequestFactory requestFactory =

RequestFactory.parseAnnotations(retrofit, method);

Type returnType = method.getGenericReturnType();

//第二步

return HttpServiceMethod.parseAnnotations(retrofit,

method, requestFactory);

}

第一步:

通过RequestFactory的parseAnnotations()解析method(ApiService的method)中的注解信息,具体代码很简单就不再贴了。不过需要注意这一步只是解析注解并保存在RequestFactory工厂中,会在请求时再通过RequestFactory将请求信息做拼装。

第二步:

调用HttpServiceMethod的parseAnnotations创建ServiceMethod,这个方法很长并且信息量很大,下一小节我再详细描述,此处你只需知道它做了什么即可。其实到这方法调用链已经很绕了,我先帮大家捋一下 HttpServiceMethod其实是ServiceMethod的子类,Retrofit动态代理里面的loadServiceMethod就是HttpServiceMethod类型对象,最后来看一下它的invoke()方法。

#HttpServiceMethod.class

@Override

final @Nullable ReturnT invoke(Object[] args) {

Call call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);

return adapt(call, args);

}

创建了一个OkHttpCall实例,它内部其实就是对OkHttp的一系列操作,这里先按住不表后面我会再提到。把关注点切到返回值,返回的Call对象没做任何操作,而是传入到adapter()方法一并返回来,字面意思应该是一个适配操作,那究竟如何适配?这里再埋一个伏笔与3.1结尾相呼应,下一小节我们再一一揭开。

动态代理讲完了,那么它解决了什么问题?

假如不使用代理模式,那关于ApiService中方法注解解析的操作势必会浸入到业务当中,一旦对其修改就有可能影响到业务,其实也就是也违背了我们前面所说的门面模式和迪米特法则,通过代理模式做一个切面操作(AOP)可以完美规避了这一问题。可见这里的门面模式和代理模式是相辅相成的

Retrofit事先都不知道ApiService方法数量,就算知道也避免不了逐一解析而产生大量的模版代码,此时可通过引入动态代理在运行时动态解析 从而解决这一问题。

4. ReturnT、ResponseT做一次适配的意义何在?

============================================================================================

ResponseT、ReturnT是 Retrofit 对响应数据类型和返回值类型的简称

4.1 创建HttpServiceMethod

===================================================================================

上一小节我们跟到了adapter(),这是一个抽象方法,其实现类是通过HttpServiceMethod的parseAnnotations创建的,继续跟下去:

#HttpServiceMethod.class

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(

Retrofit retrofit, Method method, RequestFactory requestFactory) {

boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;

boolean continuationWantsResponse = false;

boolean continuationBodyNullable = false;

Annotation[] annotations = method.getAnnotations();

Type adapterType;

//1.获取adapterType,默认为method返回值类型

if (isKotlinSuspendFunction) {

Type[] parameterTypes = method.getGenericParameterTypes();

Type responseType =

Utils.getParameterLowerBound(

0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);

if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {

// Unwrap the actual body type from Response.

responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);

continuationWantsResponse = true;

} else {

}

adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);

annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);

} else {

adapterType = method.getGenericReturnType();

}

//2.创建CallAdapter

CallAdapter<ResponseT, ReturnT> callAdapter =

createCallAdapter(retrofit, method, adapterType, annotations);

Type responseType = callAdapter.responseType();

//3.创建responseConverter

Converter<ResponseBody, ResponseT> responseConverter =

createResponseConverter(retrofit, method, responseType);

okhttp3.Call.Factory callFactory = retrofit.callFactory;

//4.创建HttpServiceMethod类型具体实例

if (!isKotlinSuspendFunction) {

return new HttpServiceMethod.CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);

}

//兼容kotlin suspend方法

else if (continuationWantsResponse) {

//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.

return (HttpServiceMethod<ResponseT, ReturnT>)

new HttpServiceMethod.SuspendForResponse<>(

requestFactory,

callFactory,

responseConverter,

(CallAdapter<ResponseT, Call>) callAdapter);

} else {

//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.

return (HttpServiceMethod<ResponseT, ReturnT>)

new HttpServiceMethod.SuspendForBody<>(

requestFactory,

callFactory,

responseConverter,

(CallAdapter<ResponseT, Call>) callAdapter,

continuationBodyNullable);

}

}

注释1:获取adapterType,这里的adapter指的是Retrofit构建时通过addCallAdapterFactory()添加的类型,如果添加的是RxJava那adapterType便是Observable。默认是method返回值,同时也会做kotlin suspend适配

注释2:创建callAdapter,暂时掠过,下面详细描述

注释3:创建responseConverter,暂时掠过,下面详细描述

注释4:这里会创建具体的HttpServiceMethod类型实例,总共有三种类型CallAdapted、SuspendForResponse、SuspendForBody,第一种为默认类型,后两种可兼容kotlin suspend。内部主要做的事情其实很简单,就是通过内部的adapter()调用callAdapter->adapter(),具体代码就不贴了,感兴趣的自行查看

4.2 如何管理callAdapter、responseConverter?

==================================================================================================

创建创建callAdapter

#HttpServiceMethod.class

private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(

Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {

return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);

}

通过retrofit#callAdapter()获取CallAdapter,继续跟

#Retrofit.class

public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {

return nextCallAdapter(null, returnType, annotations);

}

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++) {

//通过returnType在callAdapterFactories获取adapter工厂,再get adapter

CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);

if (adapter != null) {

return adapter;

}

}

}

先通过returnType在callAdapterFactories获取adapter工厂,再通过工厂get()获取CallAdapter实例。callAdapterFactories是3.1结尾build()中初始化的,通过platform添加默认类型,也可以通过addCallAdapterFactory()添加RxJava之类的适配器类型。

这里用到了两个设计模式适配器跟策略

  • 适配器模式

返回的CallAdapter其实就是Call的适配器,假如你想让Retrofit配合RxJava使用,常规方式只能在业务中单独创建Observable并与Call融合,关于Observable与Call融合(适配)其实是与业务无关的,此时可以引入适配器模式将Call适配成Observable,将适配细节从业务层挪到Retrofit内部,符合迪米特法则

  • 策略模式

通过ReturnT获取对应的CallAdapter,如果ReturnT是Call那获取的是DefaultCallAdapterFactory创建的实例,如果是Observable则获取的是RxJava2CallAdapterFactory创建的实例。假如想新增一种适配器只需明确ReturnT,创建对应工厂再通过addCallAdapterFactory添加即可,Retrofit会通过ReturnT自动寻找对应CallAdapter,符合开闭原则(扩展开放)

创建responseConverter

关于responseConverter其实是做数据转换的,可以将ResponseT适配成我们想要的数据类型,比如Gson解析只需通过addConverterFactory添加GsonConverterFactory创建的Converter实例即可 具体添加、获取流程与CallAdapter基本一致,感兴趣的同学可自行查看

4.3 发起请求

====================================================================

到上一小结我们已经创建了所有需要的内容,再回到HttpServiceMethod的invoke,这里会将OkHttpCall传入到adapt执行并返回,HttpServiceMethod的实现类的adapter会执行对应CallAdapter的adapter我们就取默认的CallAdapter 即DefaultCallAdapterFactory通过get获取的CallAdapter,代码如下:

DefaultCallAdapterFactory.class

public @Nullable CallAdapter<?, ?> get(

最后

最后这里放上我这段时间复习的资料,这个资料也是偶然一位朋友分享给我的,里面包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

还有 高级架构技术进阶脑图、高级进阶架构资料 帮助大家学习提升进阶,也可以分享给身边好友一起学习。

一起互勉~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

的实现类的adapter会执行对应CallAdapter的adapter我们就取默认的CallAdapter 即DefaultCallAdapterFactory通过get获取的CallAdapter,代码如下:

DefaultCallAdapterFactory.class

public @Nullable CallAdapter<?, ?> get(

最后

最后这里放上我这段时间复习的资料,这个资料也是偶然一位朋友分享给我的,里面包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

还有 高级架构技术进阶脑图、高级进阶架构资料 帮助大家学习提升进阶,也可以分享给身边好友一起学习。

[外链图片转存中…(img-fk5qVh9X-1715368676262)]

[外链图片转存中…(img-MGcV3XTm-1715368676262)]

一起互勉~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值