横观历史

一点感概

记得当年刚入行Android,让我历历在目的框架android-async-http,过后用的不可开交,随着工夫的变迁,官网的新宠Volley诞生,不久的不久官网发表本人放弃,坑爹,Android 4.4后,HttpURLConnection底层实现改为OkHttp,随即OkHttp是各个大牛封装的根基,Retrofit最为出名,能够说简直没有人没用过,起初不晓得谁刮起了RxJava大风,变成了Retrofit+RxJava+OkHttp,到目前为止我都很恶感RxJava,框架诚然很好,但咱们不适合,不爱就是不爱,没必要牵强。自从有了Coroutines协程,算是找到了最优解,为什么这么说呢?咱们先来剖析下这几种实现形式

android-async-http

基于Apache的HttpClient库构建的Android异步网络框架,大抵用法如下:

 private AsyncHttpClient asyncHttpClient = new AsyncHttpClient() {        @Override        protected AsyncHttpRequest newAsyncHttpRequest(DefaultHttpClient client, HttpContext httpContext, HttpUriRequest uriRequest, String contentType, ResponseHandlerInterface responseHandler, Context context) {            AsyncHttpRequest httpRequest = getHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context);            return httpRequest == null                    ? super.newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context)                    : httpRequest;        }    };  @Override    public RequestHandle executeSample(AsyncHttpClient client, String URL, Header[] headers, HttpEntity entity, ResponseHandlerInterface responseHandler) {        return client.get(this, URL, headers, null, responseHandler);    }    @Override    public String getDefaultURL() {        return "https://httpbin.org/get";    } @Override    public ResponseHandlerInterface getResponseHandler() {        return new AsyncHttpResponseHandler() {            @Override            public void onStart() {                clearOutputs();            }            @Override            public void onSuccess(int statusCode, Header[] headers, byte[] response) {                debugHeaders(LOG_TAG, headers);                debugStatusCode(LOG_TAG, statusCode);                debugResponse(LOG_TAG, new String(response));            }            @Override            public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {                debugHeaders(LOG_TAG, headers);                debugStatusCode(LOG_TAG, statusCode);                debugThrowable(LOG_TAG, e);                if (errorResponse != null) {                    debugResponse(LOG_TAG, new String(errorResponse));                }            }            @Override            public void onRetry(int retryNo) {                Toast.makeText(GetSample.this,                        String.format(Locale.US, "Request is retried, retry no. %d", retryNo),                        Toast.LENGTH_SHORT)                        .show();            }        };    }

咱们暂且不提当初官网当初曾经不必HttpClient,框架自身有很多能够借鉴的优良设计,放在当初堪称是功能丰富,十分稳固,且bug极少。我少啰嗦几句,间接看下一个实现,最终咱们再宏观的看看,到底网络框架的前世今生是什么走向。

VolleyPlus

VolleyPlus库对Volley进行的我的项目改良以及残缺的图像缓存,波及应用RequestQueue,RequestTickle和Request

RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {    @Override    public void onResponse(String response) {        ....    }}, new Response.ErrorListener() {    @Override    public void onErrorResponse(VolleyError error) {        ....    }});mRequestQueue.add(stringRequest);

比起AsyncHttpClient,退出了申请队列(AsyncHttpClient也有线程池),线程调度,缓存DiskLruCache,反对的缓存类型:

  • 网络缓存
  • 资源缓存
  • 文件缓存
  • 视频缓存
  • 内容URI缓存

等等吧,也是给咱们网络层提供了不少的遍历,接下来看看Retrofit+RxJava

Retrofit+RxJava

public interface ApiService {    @GET("demo")    Observable<Demo> getDemo(@Query("start") int start, @Query("count") int count);}        // 应用例子        apiService.getDemo(0, 10)                .subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(new Observer<Demo>() {                    @Override                    public void onSubscribe(Disposable d) {                        Log.d(TAG, "onSubscribe: ");                    }                    @Override                    public void onNext(Demo demo) {                        Log.d(TAG, "onNext: " + demo.getTitle());                        List<Subjects> list = demo.getSubjects();                        for (Subjects sub : list) {                            Log.d(TAG, "onNext: " + sub.getId() + "," + sub.getYear() + "," + sub.getTitle());                        }                    }                    @Override                    public void onError(Throwable e) {                        Log.e(TAG, "onError: " + e.getMessage());                    }                    @Override                    public void onComplete() {                        Log.d(TAG, "onComplete: Over!");                    }                });

这种仿佛是很多很多App目前的应用形式,毋庸置疑,它解决了开发中网络层的很多问题,那为什么它会这么火呢?它背地的实质是什么?我这里啰嗦几句哈,大家应该都据说过后端的开发框架Spring,说到Spring,你必定会想到它设计的两个外围概念,IOC管制反转,AOP面向切面编程,Retrofit实质上用IOC管制反转,你只须要定义接口,对象由框架自身负责管理,接口有个特点就是不变,利用IOC使得你不得不定义接口,这样间接进步代码的稳定性,是不是很拜服大佬的思维,在这感激这些大佬们的致力,让咱们缓缓养成一个好的编程习惯,当然咱们也更应该关注他们设计的思维,这样咱们在本人做框架的时候,是不是能够借鉴(copy)一下呢。好了,明天的配角上场了,来看下最新的实现

Net(okhttp+coroutines+lifecycle)

Get

          scopeNetLife {            // 该申请是谬误的门路会在控制台打印出错误信息            Get<String>("error").await()        }

Post

          scopeDialog {            tv_fragment.text = Post<String>("dialog") {                param("u_name", "drake")                param("pwd", "123456")            }.await()        }

这么简略的实现蕴含了什么?

  1. 生命周期监控
  2. 加载中显示框
  3. 主动序列化
  4. 异样解决
  5. 协程同步
  6. 非线程阻塞

劣势很显著,代码确实很简洁,让他们浏览代码不吃力,性能更欠缺,无需切换线程,打消相似Rxjava的observeOn,subscribe匿名外部类的模板代码,让人更加重视业务逻辑的编写。想必这就是我认为的最优解的一些理由吧。你必定也会说Rxjava也有这些性能(如:lifecycle),是的,你想怎么用是你的权力,我不能左右。我只想说

,你饿了吗?哈哈 

小结

通过几个版本的代码比照,你是不是曾经察觉到,网络的前世今生,让咱们一起总结下进化的特点

  1. 简单化
  2. 生命感知力
  3. 更轻量级的调度
  4. 自我管理
  5. 高内聚(暗藏实现细节)
  6. 高可定制

这就是咱们网络框架的今生,你不重构一下吗?亲。

老师说常常教咱们知其然知其所以然,那么我就带你走进Net框架实现的底层,让咱们看看作者都做了些什么的封装。

源码剖析

首先来看下我的项目的目录构造

我的项目次要分为两个Lib

  • Kalle https://github.com/yanzhenjie/Kalle Alibaba YanZhenjie 大佬写的,对okhttp的再次包装,具体文档见:https://yanzhenjie.com/Kalle/
  • Net 此我的项目的外围实现

Kalle的源码咱们前期再剖析,尽管曾经一年零两个月未保护了,但毕竟大佬做的,有很多能够学习的中央,这期咱们针对强哥的框架Net做深刻的剖析,因为自己最近两年始终在做Rom相干,对Jetpack相干的应用也不是特地的相熟,有什么不对的还请各位手下留情,强哥说他还对Kalle的源码做了变更,所以才拿到我的项目里保护,前期还会降级一下okhttp的版本,因为YanZhenjie没有降级最新的okhttp,当然这不是咱们的重点,上面咱们来开始剖析源码

再来看下强哥总结的框架个性

  • 协程
  • 并发网络申请
  • 串行网络申请
  • 切换线程
  • DSL编程
  • 不便的缓存解决
  • 主动错误信息吐司
  • 主动异样捕捉
  • 主动日志打印异样
  • 主动JSON解析
  • 主动解决下拉刷新和上拉加载
  • 主动解决分页加载
  • 主动缺省页
  • 主动解决生命周期
  • 主动解决加载对话框
  • 协程作用域反对谬误和完结回调
  • 反对先强制读取缓存后网络申请二次刷新
  • 附带超强轮循器

最近强哥还加了个优先队列,同时发动10个申请,优先取第一个回来的后果,而且代码极其简洁好用。

Net源码目录

22个文件,不多不少,刚刚好,整体看目录,基本不必深刻看代码,咱们就能清晰的晓得这是干什么的,这就是一种好的目录构造,值得学习

  • connect 实在的链接对象
  • convert 序列化
  • error 异样定义
  • scope 作用域
  • tag 日志tag
  • time 工夫相干(联合它的个性,轮训应该在这里有相干实现)
  • utils 工具类
  • Net.kt 网络申请的外围实现
  • NetConfig.kt 全局配置文件

类图

一张清晰可见的类图,是剖析一个源码无利的伎俩,随我将其源码类图搞个明确,请看下图

通过类图,咱们能够清晰看到,该框架源码的类构造,我大抵分以下模块,好对立剖析

  • kalle扩大反对局部:ConnectFactory、Converter、NetException
  • 作用域局部:CoroutineScope联合Android Lifecycle的扩大,以及Sate、Page、Dialog对NetCoroutineScope的实现
  • 动静扩大局部,这是框架的外围:Net,组合协程、Kalle实现新的网络框架
  • 配置、工具局部:NetConfig、Utils(省略未画)、Interval

接下来,具体看下各个模块的实现

kalle扩大反对局部

ConnectFactory只有一个性能:就是创立一个Connection对象,参数是Request。这里为什么这样做呢?其实是为了能兼容不同的URLConnection,强哥应用的okhttp,天然应用HttpClient

Converter就是大家相熟的Response解决,这里有个奇妙的设计,将接口返回的数据,通过传入的code作为key,默认是code,取到业务定义的code码,与传入的success参数比照,如果一样就是胜利,而后parsebody,如果返回其余,则抛出失败的后果。

NetException 异样对于一个框架来说简直不可避免,自定义的异样信息,有利于问题的归类和追踪。上图中就是作者自定义的RequestParamsException申请参数异样、ServerResponseException服务器异样,这个代码就不必看了,大家必定是明确了,上面来看作用域局部

作用域局部

作用域都继承自CoroutineScope,CoroutineScope持有CoroutineContext上下文,其实不难理解,就像Android的Context,别离有Applicaition Context和Activity Context,其实就是标示出不同的生命周期,那么顺着这个了解就好说了,作者形象AndroidScope,其实就是为了监听Lifecycle的生命周期,而后在默认的ON_DESTROY函数回调中cancel掉协程,如下图代码所示

看到了吧,继承CoroutineScope须要实现CoroutineContext对吧,那这里的context是啥呢?

协程上下文蕴含以后协程scope的信息,如CoroutineDispatcher,CoroutineExceptionHandler,Job,其实这三个都是继承实现CoroutineContext.Element,而这个+号不是咱们认为的加号,点击后看到如下源码,这里奇妙的使用操作符重载,不了解概念能够点击该链接:https://www.kotlincn.net/docs/reference/operator-overloading.html 学习。

咱们先不钻研这样做的用意哈,这里你要晓得的是,该协程上下文承载了三个对象

  • Dispatchers.Main 主线程
  • exceptionHandler 异样捕捉
  • SupervisorJob 须要执行的协程

SupervisorJob 它相似于惯例的 Job,惟一的不同是:SupervisorJob 的勾销只会向下流传。看下官网的例子

import kotlinx.coroutines.*fun main() = runBlocking {    val supervisor = SupervisorJob()    with(CoroutineScope(coroutineContext + supervisor)) {        // 启动第一个子作业——这个示例将会疏忽它的异样(不要在实践中这么做!)        val firstChild = launch(CoroutineExceptionHandler { _, _ ->  }) {            println("The first child is failing")            throw AssertionError("The first child is cancelled")        }        // 启动第二个子作业        val secondChild = launch {            firstChild.join()            // 勾销了第一个子作业且没有流传给第二个子作业            println("The first child is cancelled: ${firstChild.isCancelled}, but the second one is still active")            try {                delay(Long.MAX_VALUE)            } finally {                // 然而勾销了监督的流传                println("The second child is cancelled because the supervisor was cancelled")            }        }        // 期待直到第一个子作业失败且执行实现        firstChild.join()        println("Cancelling the supervisor")        supervisor.cancel()        secondChild.join()    }}

这段代码的输入如下:

The first child is failing
The first child is cancelled: true, but the second one is still active
Cancelling the supervisor
The second child is cancelled because

其实我没怎么了解向下流传,哪位大佬能解释解释,咱们接着往下剖析。

作者形象这些高级函数,用于实现dsl的成果如:

catch用于捕捉协程异样信息处理,finally是在invokeOnCompletion里触发,invokeOnCompletion在协程进入实现状态时触发,包含异样和失常实现。

好滴,AndroidScope大抵就剖析完了,咱们来简略总结一下

  • AndroidScope 在lifecycle ON_DESTROY时 主动cancel
  • 能够监听异样catch,能够实现finally
  • 主线程执行,作用域内能够刷新UI,不必切换线程

就这些,再来看下NetCoroutineScope,它是网络框架的扩大重心,NetCoroutineScope同样的具备主动勾销的逻辑,如:

看上面的变量,咱们其实能晓得,NetCoroutineScope次要是扩大了网络的缓存策略,需不需要缓存,是否缓存胜利等等,还有个animate,这里看作者正文,是管制是否在缓存胜利后仍然显示加载动画

而后笼罩launch函数,将逻辑插入到外面,其实这里违反了里氏替换准则,子类尽量不要重写父类的办法,继承蕴含这样一层含意:父类中但凡曾经实现好的办法(绝对于形象办法而言),实际上是在设定一系列的标准和契约,尽管它不强制要求所有的子类必须听从这些契约,然而如果子类对这些非形象办法任意批改,就会对整个继承体系造成毁坏。而里氏替换准则就是表白了这一层含意。

而后就是cache配置的实现

用例:

用起来简略的一批

接下来就是StateCoroutineScope、PageCoroutineScope、DialogCoroutineScope,这三个我就不一一解读了,同样的套路,对理论业务中的高发场景做的高度形象。如:StateCoroutineScope是对StateLayout的扩大解决,在StateLayout onViewDetachedFromWindow时 主动cancel()掉协程,等等吧。

Net的外围局部,动静扩大

不啰嗦,间接看代码哦

  • 扩大CoroutineScope,增加get函数
  • 内连函数,reified润饰M真泛型,真泛型不了解的能够看大佬的掘金介绍:https://juejin.im/post/6844904030662033422
  • 执行完后对uid做cancel或者remove解决,开释缓存
  • 联合NetConfig的host+path,造成残缺的申请门路
  • SimpleUrlRequest创立新的申请
  • async(Dispatchers.IO) 子线程的网络申请

Post同理,其实差异就在申请参数,其余Head、Options 就不必剖析喽

剖析到这里,咱们再总结一发

  • 作用域局部继承扩大自CoroutineScope
  • Net局部动静扩大自CoroutineScope

两局部如同有关联,其实没有任何的代码耦合,这也是十分值得学习的中央。这样做的益处其实就是限度了申请在作用域之外,造成代码的不标准。如图,在作用域外基本申请不了

好了,这部分根本就完结了,接下来看下源码中最初一部分

配置、工具局部

NetConfig对象缓存网络的配置,如域名,app上下文,弹窗,谬误,StateLayout的全局缺省页

  • initNet 初始化
  • onError 全局谬误信息处理
  • onStateError 缺省页解决

用法:

在Application onCreate函数中初始化就行了,就这样就完了,没没没,还有,来看个例子

为什么有个ScopeNetLife,不应该是NetCoroutineScope吗?对的其实就是它,实现在这里,咱们一起来看下

这个是Utils中的ScopeUtils类,同样是相熟的动静扩大,简略的扩大,实现DSL格调就是这里的后果。

好了,基本上都讲完了吧,通过一系列的剖析,你是不是曾经按耐不住本人要去体验了呢?

源码地址

强哥首页:https://github.com/liangjingkanji

Net源码:https://github.com/liangjingkanji/Net

欢送关注骚扰哦,据说强哥最近有大动作,将来Net会更加的好用,也心愿你能喜爱这次解说,辛苦你点个赞呗,感激。

作者

i校长

简书 https://www.jianshu.com/u/77699cd41b28

掘金 https://juejin.im/user/131597127135687

集体网站 http://jetpack.net.cn 、 http://ibaozi.cn