OKHttp实战篇(内部附赠真实案例+源码大公开)

Posted Coder崛起

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OKHttp实战篇(内部附赠真实案例+源码大公开)相关的知识,希望对你有一定的参考价值。





阅读人群

本系列针对具有以下经历的同学:

1.从事android开发,有OkHttp使用基础的人


OKHttp实战篇(内部附赠真实案例+源码大公开)



背景

/ EXPERIENCE SHARING


很多时候我们都知道学习一个框架,会用是远远不够的,起码的一点就是要对框架的源码进行不断阅读,可是源码的学习只是让我们来应付面试的嘛?其实这就失去了学习的意义;因为在我们实际生活用不到迟早还是会忘记和没有学习一样!

所以,笔者认为一个框架阅读源码过后,认清这些框架中的拓展点,即可以让我们进行自定义点,才是重中之重;因为这些点可以帮助我们在使用框架时,发挥到最大的作用!

而OKhttp备受大家喜爱的一点便是它的拦截器了,这便是我们可以定制最方便的地方, 所以这里为大家提供几种我在公司中针对拦截器的实际应用方式,让大家感受一下OKHttp通过拦截器的方式给大家提供便利性的作用!


01

招式一:记录网路信息


背景

笔者的公司在开发过程中经常和后台对接网络接口时,经常会有这样的对话(PS:笔者负责的app对接的接口有对接加密):

笔者:XXX接口逻辑有问题后台:麻烦把明文的报文和加密的报文都发一下,调试一下笔者:xxx接口的request 是.... ,response是.......

方案

首先,根据上面的场景,看得出我们一般都会有需要时刻获取到每一条请求的明文和密文的情况;所以,一般我们想到的最直接的办法就是解决以上情况的方式就是将request和response的内容都打在控制台上,方便可以随时复制;那么这种场景很明显就可以用Interceptor来解决问题,那么这个解决思路如下:

1.根据当前场景,我们要首先有一个对请求响应处理加解密的 application级别的interceptor(ps:加解密只需要进行一次,故不选择网络级别拦截器) 2.根据我们上文我们可以知道,拦截器链上依此按照每一个拦截器添加的顺序依次执行的,故我们的思路就是在加密拦截器前后各添加一个记录日志的拦截器,从而得到两种请求

代码如下:

OkHttpClient client = new OkHttpClient.Builder() //添加了明文的日志拦截器 .addInterceptor(new ClearTextLoggingInterceptor()) //添加了加解密 .addInterceptor(new CodecInterceptor()) //添加了密文的日志拦截器 .addInterceptor(new EncrypteLoggingInterceptor()) .build();

为了方便理解可以看一下示意图


OKHttp实战篇(内部附赠真实案例+源码大公开)

由此我们可以感觉到这种责任链的模式给我们提供的对于 request和response的丰富的客户化拓展性,并且我们可以通过这样的方式将我们想要定制化的效果也分工在每一个类中,然后按照顺序依次执行;这样有以下的好处:

1.当客户化的功能被拆分在每一个拦截器后,方便用户在后期维护中针对功能的增加或者减少2.提供了每一个类的清晰的可读性,不用把大量的客户化代码都放置到一个类

02

招式二:用户请求偷天换日


背景

在笔者在公司以下一系列问题:

1.公司由于是to B的业务较多;业务逻辑非常复杂,为我们的app制造数据成了一个成本非常高的问题2.我们的后台人员资源不足导致我们并行开发对接时,接口给到的时间也经常比较晚,造成前端开发人员时间前松后紧3.笔者同时还在负责着一部分的app的自动化测试,同样的数据问题限制着UI自动化的进行,致使脚本运行前会需要做大量准备工作,准备好app需要消费的数据

而业界比较公认的一种解决这类问题的方案就是 MOCK数据

但是,当尝试解决此问题时,又有了一只新的拦路虎产生,那就是app的网络请求是加密的,并且由于历史原因各个app的加密方案还不一样,这两者造成的伤害就十分巨大了;因为它会产生这样的以下问题:

1.如果加密方案是一致的,用一个比较简单粗暴的方式就是将我们的加解密方案,在我们自己搭建的mock平台客户化后移植进去,且针对不同项目还要加解密的key配置不一样的2.app要做到多套baseUrl的自由切换,实现mock与真实的请求的互动,又或者可以在wifi中搭建代理服务器帮助切换真实和MOCK的数据,但是两者都具有高昂的成本

所以,基于以上的背景,我们考虑在服务端去解决这样的问题,虽然实现了可以做到客户端无感知,且代码没有入侵性;但是实际上确实付出的代价十分高昂,故我们设计这样的方案:

首先,我们知道

Response response = client.newCall(request).execute()

实际是被这样的一层一层的拦截器处理过后,才返回给用户的response

 interceptors.addAll(client.interceptors()); interceptors.add(new RetryAndFollowUpInterceptor(client)); interceptors.add(new BridgeInterceptor(client.cookieJar())); interceptors.add(new CacheInterceptor(client.internalCache())); interceptors.add(new ConnectInterceptor(client)); interceptors.addAll(client.networkInterceptors());  interceptors.add(new CallServerInterceptor(forWebSocket));

而在拦截器中

class SomeInterceptor implements Interceptor { @Override public Response intercept(Interceptor.Chain chain) throws IOException { Request request = chain.request(); //调用此行代码去链式调用下一个拦截器 Response response = chain.proceed(request); return response; }

所以,我们可以得到一个结论就是,每一个拦截器可以决定我的是否要链式调用下一层的拦截器,同时也可以决定我这一层的拦截器response数据来自哪里,所以我们有了以下的方案:

OKHttp实战篇(内部附赠真实案例+源码大公开)

从图上可以看到用户的request经过了一些常规的拦截器后,在我们的加解密拦截器前添加了一个mock拦截器,在判断了用户的请求是否需要走mock后,决定用户的response是从真实网络请求中获取,还是从mock server的平台获取,而这个思路充分体现了 责任链模式中,由责任链的每一环节决定分配的任务是否是由自己处理还是分发给下一层处理,而对调用者来说这些都是透明毫无感知的!

转换成代码

class MockInterceptor implements Interceptor { @Override public Response intercept(Interceptor.Chain chain) throws IOException { Request request = chain.request(); //判断是否需要mock if(isNeedMock(request)){ //读取原始request构建对应的MOCK server的request Request mockRequest = buildMockRequest(request); //访问mockServer return MockServer(mockRequest); } //调用此行代码去链式调用下一个拦截器 Response response = chain.proceed(request); return response; }
private boolean isNeedMock(){ /*此方法主要是用来控制哪些request走假数据,哪些request走真实数据,方便开发自己切换;读者可以根据自己需求自行设计,笔者此处仅提供一个思路*/ }
private Request buildMockRequest( Request request){ /*该方法的主要作用在于我们要把真实的request替换成访问我们自行搭建的mock server平台的请求,一般是替换掉请求的baseUrl部分(即ip地址部分);其他的替换根据读者选用的mock平台自行决定 */ } /** *这一部分的就非常简单了,发送一个网络请求给 mockServer把mockServer的response返回用户 **/ private Response MockServer(Request request) throw Exeption{ return client.newCall(request).execute()); }

此种方案的优劣如下

优势:

1.MOCK SERVER 平台根据项目情况自行选择,由于不需要关心app的本身加密解密,那么基本很多MOCK平台的初始功能就能满足需要,节省了大量了阅读开源mock平台的时间,以及在平台对接各个app加解密的时间

     2.真实的接口和mock接口切换靠app端自行代码中随意切换,具体策略由移动端开发人员自己设计;对于大部分移动端同学来说在自己代码中嵌入一些内容,相比搭建代理服务器在并且在服务器的工程写后台代码耗费的时间成本,精力成本远远小的多     3.基于okhttp的拦截器设计,方便随时随时添加移除;对于调用者却毫无感知!这意味着一旦产生问题,或者需要有所改动,我们所有关注点只需要集中在mock拦截器上,代码维护成本十分低廉!


劣势:

1.  需要在业务代码中嵌入一些方便开发者使用的工具有一定的侵入性和危险性;这部分如果我们有一个合理的开关,可以控制危险到一个可控范围

      2.跳过了不同app的加密部分的问题,但是每一个要用mockServer的app都需要做相类似的定制化操作;不过,如果经过良好的设计,这部分完全可以考虑提取成一个类库方便其他app集成,这样便可以大大降低风险

当然还要搭建一个合适的mock平台,笔者选用的是 EASY-MOCK[1],感兴趣的同学可以自行了解下~

References

[1] EASY-MOCK: https://github.com/easy-mock/easy-mock


    总结   

/ SUMMARY


方便大家在能够深入理解Okhttp,同时可以使用这个类库,让APP的Mock数据,不再困难!文末也给大家看下这个实现的效果,App接口真实数据和mock数据来回切换,毫无感知哟~ (PS:文末点击 阅读原文 有更详细的OKhttp内容分析)


来源:王昊
编辑: 王昊(侵删)
图片来源:网络(侵删)

以上是关于OKHttp实战篇(内部附赠真实案例+源码大公开)的主要内容,如果未能解决你的问题,请参考以下文章

承载亿级流量的开发框架,闲鱼 Flutter 技术解析与实战大公开 | 开发者说·DTalk

秒杀系统企业级实战应用(真实工业界案例)(完整版105讲,附源码课件)

秒杀系统企业级实战应用(真实工业界案例)(完整版105讲,附源码课件)

spring-cloud-square源码速读(retrofit + okhttp篇)

spring-cloud-square源码速读(retrofit + okhttp篇)

Netty4.x实战专题案例文章列表