Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> NoHttp和OkHttp的無縫結合 NoHttp框架作者帶你看源碼(二)

NoHttp和OkHttp的無縫結合 NoHttp框架作者帶你看源碼(二)

編輯:關於Android編程

上一次帶大家分析了NoHttp源碼,知道我們可以替換NoHttp的底層為其他任何庫,例如OkHttp、HttpURLConnection、HttpClient,那今天就帶領大家一步步來實現替換NoHttp的底層為OkHttp。


NoHttp4OkHttp的介紹

先要告訴大家的是,NoHttp和OkHttp的完美結合庫我已經封裝好了,並且做了開源,起名叫NoHttp4OkHtp

NoHttp4OkHtp開源地址:https://github.com/yanzhenjie/NoHttp4OkHttp。

如何引用

使用Gradle遠程依賴,推薦

  com.yanzhenjie.nohttp
  nohttp-okhttp
  1.0.0
  pom
使用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 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,歡迎來一起討論一起進步。

 
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved