首页 > 代码库 > 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接口,但是由于篇幅关系,但是只说了原理,没有具体分析实现类的源码,为了不让大家思绪断开,这里我们还是复习一下。

上次说到IRestConnectionRestProtocol调用,负责具体的网络请求、发送数据、拿到服务器的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中确实是调用了IRestConnectiongetConnection()方法去拿到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,里面只有简单的几个类,实现了两个基类:HttpURLConnectionHttpsURLConnection

所以我们今天也会用到这个项目,首先先在gradle中引用okhttp-urlconnectionNoHttp

compile ‘com.squareup.okhttp3:okhttp-urlconnection:3.4.1‘
compile ‘com.yolanda.nohttp:nohttp:1.0.6‘

我们知道URLConnection对http和https的处理的类分别是:HttpURLConnectionHttpsURLConnection,所以OkHttp也为这两个类提供了实现:OkHttpURLConnectionOkHttpsURLConnection。因此之前用HttpURLConnectionHttpsURLConnection的地方全部替换为OkHttpURLConnectionOkHttpsURLConnection即可。

先来看看用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提供的默认实现类是怎么做的了。中选IRestConnectionCtrl + T后发现IRestConnection的默认实现类是RestConnection,我已经替大家找好了RestConnectiongetConnection()方法中一步步调用后最终走网络的地方是:

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

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    NoHttp和OkHttp的无缝结合 NoHttp框架作者带你看源码(二)