Android 網絡--我是怎么做的: Volley+OkHttp+Https

Volley 已經發布很長時間了, 也已被廣泛應用, 相關教程到處都是. 本文只說兩個值得注意的地方.
本文講解部分比較少, 請參閱提供的相關鏈接. 完整的實現代碼在 [Github dodocat/AndroidNetworkDemo] 可能看起來比這里更清晰.

使用 OkHttp 作為傳輸層的實現.

Volley 默認根據 Android 系統版本使用不同的 Http 傳輸協議實現.
在 Android 2.3以下使用 ApacheHttpStack 作為傳輸協議, 在 3.0 及以下使用 HttpURLConnection 作為傳輸層協議 (感謝評論中指正的朋友).

OkHttp 相較于其它的實現有以下的優點.

  • 支持SPDY,允許連接同一主機的所有請求分享一個socket。
  • 如果SPDY不可用,會使用連接池減少請求延遲。
  • 使用GZIP壓縮下載內容,且壓縮操作對用戶是透明的。
  • 利用響應緩存來避免重復的網絡請求。
  • 當網絡出現問題的時候,OKHttp會依然有效,它將從常見的連接問題當中恢復。
  • 如果你的服務端有多個IP地址,當第一個地址連接失敗時,OKHttp會嘗試連接其他的地址,這對IPV4和IPV6以及寄宿在多個數據中心的服務而言,是非常有必要的。

因此使用 OkHttp 作為替代是好的選擇.

  1. 首先用 OkHttp 實現一個新的 HurlStack 用于構建 Volley 的 requestQueue.

public class OkHttpStack extends HurlStack {

private OkHttpClient okHttpClient;

/**
 * Create a OkHttpStack with default OkHttpClient.
 */
public OkHttpStack() {
    this(new OkHttpClient());
}

/**
 * Create a OkHttpStack with a custom OkHttpClient
 * @param okHttpClient Custom OkHttpClient, NonNull
 */
public OkHttpStack(OkHttpClient okHttpClient) {
    this.okHttpClient = okHttpClient;
}

@Override
protected HttpURLConnection createConnection(URL url) throws IOException {
    OkUrlFactory okUrlFactory = new OkUrlFactory(okHttpClient);
    return okUrlFactory.open(url);
}

}


1. 然后使用 OkHttpStack 創建新的 Volley requestQueue. 
    ``` java
requestQueue = Volley.newRequestQueue(getContext(), new OkHttpStack());
requestQueue.start();

這樣就行了.

使用 Https

作為一個有節操的開發者應該使用 Https 來保護用戶的數據, Android 開發者網站上文章[Security with HTTPS and SSL]做了詳盡的闡述.

OkHttp 自身是支持 Https 的. 參考文檔 [OkHttp Https], 直接使用上面的 OkHttpStack 就可以了, 但是如果遇到服務器開發哥哥使用了自簽名的證書(不要問我為什么要用自簽名的), 就無法正常訪問了.

網上有很多文章給出的方案是提供一個什么事情都不做的TrustManager 跳過 SSL 的驗證, 這樣做很容受到攻擊, Https 也就形同虛設了.

我采用的方案是將自簽名的證書打包入 APK 加入信任.

好處:

  • 應用難以逆向, 應用不再依賴系統的 trust store, 使得 Charles 抓包等工具失效. 要分析應用 API 必須反編譯 APK.
  • 不用額外購買證書, 省錢....

缺點:

  • 證書部署靈活性降低, 一旦變更證書必須升級程序.

實現步驟

以最著名的自簽名網站12306為例說明

  1. 導出證書

     echo | openssl s_client -connect kyfw.12306.cn:443 2>&1 |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > kyfw.12306.cn.pem
    
  2. 將證書轉為 bks 格式
    下載最新的bcprov-jdk, 執行下面的命令. storepass 是導出密鑰文件的密碼.

export CERTSTORE=kyfw.12306.cn.bks
keytool -importcert -v
-trustcacerts
-alias 0
-file <(openssl x509 -in kyfw.12306.cn.pem)
-keystore $CERTSTORE -storetype BKS
-providerclass org.bouncycastle.jce.provider.BouncyCastleProvider
-providerpath ./bcprov-jdk16-1.46.jar
-storepass asdfqaz
```

  1. 將導出的 kyfw.bks 文件放入 res/raw 文件夾下.

  2. 創建 SelfSignSslOkHttpStack

/**

  • A HttpStack implement witch can verify specified self-signed certification.
    */
    public class SelfSignSslOkHttpStack extends HurlStack {

    private OkHttpClient okHttpClient;

    private Map<String, SSLSocketFactory> socketFactoryMap;

    /**

    • Create a OkHttpStack with default OkHttpClient.
      */
      public SelfSignSslOkHttpStack(Map<String, SSLSocketFactory> factoryMap) {
      this(new OkHttpClient(), factoryMap);
      }

    /**

    • Create a OkHttpStack with a custom OkHttpClient
    • @param okHttpClient Custom OkHttpClient, NonNull
      */
      public SelfSignSslOkHttpStack(OkHttpClient okHttpClient, Map<String, SSLSocketFactory> factoryMap) {
      this.okHttpClient = okHttpClient;
      this.socketFactoryMap = factoryMap;
      }

    @Override
    protected HttpURLConnection createConnection(URL url) throws IOException {
    if ("https".equals(url.getProtocol()) && socketFactoryMap.containsKey(url.getHost())) {
    HttpsURLConnection connection = (HttpsURLConnection) new OkUrlFactory(okHttpClient).open(url);
    connection.setSSLSocketFactory(socketFactoryMap.get(url.getHost()));
    return connection;
    } else {
    return new OkUrlFactory(okHttpClient).open(url);
    }
    }
    }

    
    
  1. 然后用 SelfSignSslOkHttpStack 創建 Volley 的 RequestQueue.

    String[] hosts = {"kyfw.12306.cn"};
    int[] certRes = {R.raw.kyfw};
    String[] certPass = {"asdfqaz"};
    socketFactoryMap = new Hashtable<>(hosts.length);
    
    for (int i = 0; i < certRes.length; i++) {
        int res = certRes[i];
        String password = certPass[i];
        SSLSocketFactory sslSocketFactory = createSSLSocketFactory(context, res, password);
        socketFactoryMap.put(hosts[i], sslSocketFactory);
    }
    
    HurlStack stack = new SelfSignSslOkHttpStack(socketFactoryMap);
    
    requestQueue = Volley.newRequestQueue(context, stack);
    requestQueue.start();
    
  2. 我們來試一試, 用上一步穿件的 RequestQueue 替換掉原來的, 然后發請求試試.

    
        StringRequest request = new StringRequest(
                Request.Method.GET,
                "https://kyfw.12306.cn/otn/",
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        responseContentTextView.setText(response);
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        responseContentTextView.setText(error.toString());
                    }
                });
        RequestManager.getInstance(this).addRequest(request, this);
    

1. done


[Volley]:http://developer.android.com/training/volley/index.html
[OkHttp]:http://square.github.io/okhttp/
[Gson]:https://github.com/google/gson

[Security with HTTPS and SSL]:https://developer.android.com/training/articles/security-ssl.html
[OkHttp Https]:https://github.com/square/okhttp/wiki/HTTPS
[Github dodocat/AndroidNetworkDemo]:https://github.com/dodocat/AndroidNetworkdemo
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,348評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,083評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,706評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,442評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,802評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,983評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,542評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,287評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,486評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,710評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,116評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,412評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,224評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,462評論 2 378

推薦閱讀更多精彩內容