首页 > 代码库 > NoHttp和OkHttp的无缝结合 NoHttp框架作者带你看源码(二)
NoHttp和OkHttp的无缝结合 NoHttp框架作者带你看源码(二)
NoHttp和OkHttp的无缝结合 NoHttp框架作者带你看源码(二)
版权声明:转载必须注明本文转自严振杰的博客: http://blog.csdn.net/yanzhenjie1003
上一次带大家分析了NoHttp源码,知道我们可以替换NoHttp的底层为其他任何库,例如OkHttp、HttpURLConnection、HttpClient,那今天就带领大家一步步来实现替换NoHttp的底层为OkHttp。
NoHttp源码分析的博客:http://blog.csdn.net/yanzhenjie1003/article/details/52413226。
更多NoHttp相关文章请看NoHttp博客专栏:http://blog.csdn.net/column/details/nohttp.html。
NoHttp4OkHttp的介绍
先要告诉大家的是,NoHttp和OkHttp的完美结合库我已经封装好了,并且做了开源,起名叫NoHttp4OkHtp。
NoHttp4OkHtp开源地址:https://github.com/yanzhenjie/NoHttp4OkHttp。
如何引用
- 使用Gradle远程依赖,推荐
compile ‘com.yanzhenjie.nohttp:nohttp-okhttp:1.0.0‘
Or Maven
<dependency>
<groupId>com.yanzhenjie.nohttp</groupId>
<artifactId>nohttp-okhttp</artifactId>
<version>1.0.0</version>
<type>pom</type>
</dependency>
- 使用Jar,下载项目后导入jar文件夹中的所有jar包。不要忘记了要去NoHttp主页下载NoHttp的jar,版本为1.0.6及以上。
使用介绍
基本用法NoHttp的所有用法不变,需要注意的两个地方:
1. 创建队列NoHttp的用法是NoHttp.newRequestQueue…,使用本库改为NoOkHttp.newRequestQueue…
2. 原来的同步请求是NoHttp.startSyncRequest…,使用本库改为NoOkHttp.startSyncRequest…
如果还有不明白的可以看demo或者加QQ技术群来讨论:547839514。
其他创建请求等用法均不变,更多用法请参考NoHttp:https://github.com/yanzhenjie/NoHttp。
IRestConnection网络层
其实NoHttp源码分析那篇文章也说了IRestConnection
接口,但是由于篇幅关系,但是只说了原理,没有具体分析实现类的源码,为了不让大家思绪断开,这里我们还是复习一下。
上次说到IRestConnection
被RestProtocol
调用,负责具体的网络请求、发送数据、拿到服务器的Stream等,我们看IRestConnection
是怎么出现的,NoHttp在创建队列时需要一个IRestConnection
:
public static RequestQueue newRequestQueue(int threadPoolSize) {
// 调用下一个方法:这里传入RestConnection.getInstance()生成IRestConnection单例。
return newRequestQueue(DiskCacheStore.INSTANCE, RestConnection.getInstance(), threadPoolSize);
}
// 这里需要一个IRestConnection参数。
public static RequestQueue newRequestQueue(Cache<CacheEntity> cache, IRestConnection connection, int threadPoolSize) {
return newRequestQueue(RestProtocol.getInstance(cache, connection), threadPoolSize);
}
public static RequestQueue newRequestQueue(IRestProtocol iRestProtocol, int threadPoolSize) {
return newRequestQueue(RestParser.getInstance(iRestProtocol), threadPoolSize);
}
这里看到IRestConnection
最终被传入RestProtocol
中生成单例,而在RestProtocol
中看到:
Connection connection = iRestConnection.getConnection(request);
我们看到在RestProtocol
中确实是调用了IRestConnection
的getConnection()
方法去拿到Connection
。这里很明了,我们先看IRestConnection
的源码:
public interface IRestConnection {
/**
* 拿到网络连接的各种属性、头、流。
*/
Connection getConnection(IBasicRequest iBasicRequest);
}
看到这里,有一个返回参数Connection
,不要忘了今天为了替换NoHttp的底层为OkHttp,我们要想用OkHttp来实现这个方法,必须要看这个类是什么样的:
public interface Connection extends Closeable {
/**
* 拿到URL对象。
*/
URL getURL();
/**
* 拿到相应头。
*/
Headers responseHeaders();
/**
* 拿到服务器的输出流。
*/
InputStream serverStream();
/**
* 拿到请求过程中的异常。
*/
Exception exception();
}
这个返回参数类是一个接口,也就说明了我们可以自定义这个类,NoHttp本身提供的类我们暂且不管,这里其实我们按照下面四个方法返回值即可:一URL、一个拿服务器的响应头、一个是服务器的输出流,一个是请求过程中是否发生异常。这样就非常好替换OkHttp了,我们底层用OkHttp请求网络,然后同样给IRestConnection
返回这个对象,里面把服务器的相应头、输出流、请求过程中的异常塞进去就OK了。
OkHttp实现IRestConnection接口
上面其实是让大家从上篇博客的思路上继续,那么我们明白了原理,接下来就是具体实现了。
对OkHttp的封装
这里很重要,要想做到OkHttp和NoHttp的无缝结合,势必要对NoHttp和OkHttp有一定的了解,NoHttp的源码和网络层我们都了解了,大家更关心的是怎么使用OkHttp了。
我们要知道NoHttp的底层使用的是URLConnection
,OkHttp是一个非常优秀的底层网络框架,它是为URLConnection
提供了接口实现的,OkHttp为URLConnection
提供实现的项目叫okhttp-urlconnection
,里面只有简单的几个类,实现了两个基类:HttpURLConnection
、HttpsURLConnection
。
所以我们今天也会用到这个项目,首先先在gradle中引用okhttp-urlconnection
和NoHttp
:
compile ‘com.squareup.okhttp3:okhttp-urlconnection:3.4.1‘
compile ‘com.yolanda.nohttp:nohttp:1.0.6‘
我们知道URLConnection
对http和https的处理的类分别是:HttpURLConnection
、HttpsURLConnection
,所以OkHttp也为这两个类提供了实现:OkHttpURLConnection
、OkHttpsURLConnection
。因此之前用HttpURLConnection
和HttpsURLConnection
的地方全部替换为OkHttpURLConnection
和OkHttpsURLConnection
即可。
先来看看用HttpURLConnection
的时候请求网络的基本代码:
HttpURLConnection connection;
URL url = new URL(urlStr);
Proxy proxy = request.getProxy();
if (proxy == null) {
connection = (HttpURLConnection) url.openConnection();
} else {
connection = (HttpURLConnection) url.openConnection(proxy);
}
connection.set...
有了上面OkHttpURLConnection的引用后换成OkHttp:
OkHttpClient okhttpClient;
URL url = new URL(urlStr);
Proxy proxy = request.getProxy();
if (proxy != null) {
okhttpClient = okhttpClient.newBuilder().proxy(proxy).build();
}
HttpURLConnection connection = new OkHttpURLConnection(url, okhttpClient);
connection.set...
最重要的也就一句话的变化而已:
HttpURLConnection connection = new OkHttpURLConnection(url, okhttpClient);
因为上面用到了OkHttpClient,OkHttpClient推荐使用单例,因此基于上面的变化我给OkHttp来一个封装:
public class URLConnectionFactory {
private OkHttpClient mClient;
private static URLConnectionFactory instance;
// 单例封装。
public static URLConnectionFactory instance() {
if (instance == null) {
synchronized (URLConnectionFactory.class) {
if (instance == null) {
instance = new URLConnectionFactory();
}
}
}
return instance;
}
// 隐藏构造。
private URLConnectionFactory() {
this.mClient = new OkHttpClient();
}
// 拿到不用代理的连接。
public HttpURLConnection open(URL url) {
return open(url, mClient.proxy());
}
// 拿到需要代理的连接。
public HttpURLConnection open(URL url, Proxy proxy) {
OkHttpClient copy = mClient.newBuilder().proxy(proxy).build();
// 处理http和Https。
String protocol = url.getProtocol();
if (protocol.equals("http")) return new OkHttpURLConnection(url, copy);
if (protocol.equals("https")) return new OkHttpsURLConnection(url, copy);
// 其他连接不接受。
throw new IllegalArgumentException("Unexpected protocol: " + protocol);
}
}
这个封装过后使用起来就更简单了:
URL url = new URL(urlStr);
HttpURLConnection connection;
Proxy proxy = request.getProxy();
if (proxy == null)
connection = URLConnectionFactory.instance().open(url);
else
connection = URLConnectionFactory.instance().open(url, proxy);
connection.set...
到这里几乎没有什么悬念了,替换NoHttp底层,只需要在RestConnection
中找到调用HttpURLConnection替换为我们的封装即可,请看下面接续。
具体的实现
这里就要先看下NoHttp为IRestConnection
提供的默认实现类是怎么做的了。中选IRestConnection
后Ctrl + T
后发现IRestConnection
的默认实现类是RestConnection
,我已经替大家找好了RestConnection
的getConnection()
方法中一步步调用后最终走网络的地方是:
private HttpURLConnection createHttpURLConnection(IBasicRequest request) throws Exception {
// 1.Pre operation notice
request.onPreExecute();
// 2.Build URL
String urlStr = request.url();
URL url = new URL(urlStr);
HttpURLConnection connection;
Proxy proxy = request.getProxy();
if (proxy == null)
connection = (HttpURLConnection) url.openConnection();
else
connection = (HttpURLConnection) url.openConnection(proxy);
...
}
果不其然啊,但是别急啊,这里呢我们新建一个类OkHttpRestConnection
,然后把NoHttp原来的RestConnection
的内容拷贝过来,把最开始的构造方法和单例换成OkHttpRestConnection
既是如下:
private static OkHttpRestConnection instance;
public static IRestConnection getInstance() {
synchronized (OkHttpRestConnection.class) {
if (instance == null)
instance = new OkHttpRestConnection();
return instance;
}
}
private OkHttpRestConnection() {
}
然后把刚才网络请求的地方替换一下:
private HttpURLConnection createHttpURLConnection(IBasicRequest request) throws Exception {
// 1.Pre operation notice
request.onPreExecute();
// 2.Build URL
String urlStr = request.url();
URL url = new URL(urlStr);
HttpURLConnection connection;
Proxy proxy = request.getProxy();
if (proxy == null)
connection = URLConnectionFactory.instance().open(url);
else
connection = URLConnectionFactory.instance().open(url, proxy);
...
最后一个细节注意,因为Android系统的原因,在5.0以下下DELETE请求方法是不允许写出body的,所以NoHttp做了一个判断:
private boolean isAllowHasBody(RequestMethod requestMethod) {
boolean allowRequestBody = requestMethod.allowRequestBody();
// Fix Android bug.
if (Build.VERSION.SDK_INT < AndroidVersion.LOLLIPOP)
return allowRequestBody && requestMethod != RequestMethod.DELETE;
return allowRequestBody;
}
因为OkHttp没有这个限制,所以我们把这个方法去掉,全部换成Http协议默认的:requestMethod.allowRequestBody();
;
总结
上面封装完了,就是替换创建队列的传入的IRestConnection
了,我们只需要在创建队列时按如下调用
newRequestQueue(DiskCacheStore.INSTANCE, OkHttpRestConnection.getInstance(), 3);
第一个参数是缓存接口,第二个就是我们的网络层的接口实现,第三个队列并发数,具体可以参考上一篇博客。
这个库我已经封装好开源到Github了:https://github.com/yanzhenjie/NoHttp4OkHttp。推荐大家使用我封装好的,以后还会继续维护的。
有疑问的朋友可以加我的QQ交流群:547839514,欢迎来一起讨论一起进步。
版权声明:转载必须注明本文转自严振杰的博客: http://blog.csdn.net/yanzhenjie1003
NoHttp和OkHttp的无缝结合 NoHttp框架作者带你看源码(二)