Android實戰之你應該使用哪個網絡庫?

jopen 9年前發布 | 13K 次閱讀 Android Android開發 移動開發

本篇為翻譯文章 原文地址這里

[TOC]

前言

目前基本上每個應用都會使用HTTP/HTTPS協議來作為主要的傳輸協議來傳輸數據。即使你沒有直接使用HTTP協議,也會有成堆的SDK會包含這些協議,譬如分析、Crash反饋等等。當然,目前也有很多優秀的HTTP的協議庫,可以很方便的幫助開發者構建應用,本篇博文中會盡可能地涵蓋這些要點。Android的開發者在選擇一個合適的HTTP庫時需要考慮很多的要點,譬如在使用Apache Client或者HttpURLConnection時可能會考慮:

  • 能夠取消現有的網絡請求

  • 能夠并發請求

  • 連接池能夠復用存在的Socket連接

  • 本地對于響應的緩存

  • 簡單的異步接口來避免主線程阻塞

  • 對于REST API的封裝

  • 重連策略

  • 能夠有效地載入與傳輸圖片

  • 支持對于JSON的序列化

  • 支持SPDY、HTTP/2

歷史回眸

最早的時候Android只有兩個主要的HTTP客戶端: HttpURLConnection, Apache HTTP Client。根據Google官方博客的內容,HttpURLConnection在早期的Android版本中可能存在一些Bug:

在Froyo版本之前,HttpURLConnection包含了一些很惡心的錯誤。特別是對于關閉可讀的InputStream時候可能會污染整個連接池。

同樣,Google官方并不想轉到Apache HTTP Client中:

Apache HTTP Client中復雜的API設計讓人們根本不想用它,Android團隊并不能夠有效地工作。

而對于大部分普通開發者而言,它們覺得應該根據不同的版本使用不同的客戶端。對于Gingerbread(2.3)以及之后的版本,HttpURLConnection會是最佳的選擇,它的API更簡單并且體積更小。透明壓縮與數據緩存可以減少網絡壓力,提升速度并且能夠節約電量。當我們審視Google Volley的源代碼的時候,可以看得出來它也是根據不同的Android版本選擇了不同的底層的網絡請求庫:

if (stack == null) {
    if (Build.VERSION.SDK_INT >= 9) {
        stack = new HurlStack();
    }  else {
        // Prior to Gingerbread, HttpUrlConnection was unreliable.
        // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
        stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
    }
}

不過這樣會很讓開發者頭疼,2013年,Square為了解決這種分裂的問題發布了OkHttp。OkHttp是直接架構與Java Socket本身而沒有依賴于其他第三方庫,因此開發者可以直接用在JVM中,而不僅僅是Android。為了簡化代碼遷移速度,OkHttp也實現了類似于HttpUrlConnection與Apache Client的接口。

網絡庫對比

OkHttp獲得了巨大的社區的支持,以至于Google最終是將它作為了Android 4.4默認的Engine,并且會在5.1之后棄用Apache Client。目前OkHttp V2.5.0支持如下特性:

  • HTTP/2 以及 SPDY的支持多路復用

  • 連接池會降低并發連接數

  • 透明GZIP加密減少下載體積

  • 響應緩存避免大量重復請求

  • 同時支持同步的阻塞式調用與異步回調式調用

筆者關于OkHttp最喜歡的一點是它能夠將異步請求較好的展示:

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();

    client.newCall(request).enqueue(new Callback() {
        @Override 
        public void onFailure(Request request, Throwable throwable) {
        throwable.printStackTrace();
    }

    @Override 
      public void onResponse(Response response) throws IOException {
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
        System.out.println(response.body().string());
    }
  });
}

這個用起來非常方便,因為往往大的數據請求都不能放置在UI主線程中進行。事實上,從Android 3.0(Honeycomb 11)開始,所有的網絡操作都必須強制在單獨的線程中進行。在當時如果要把HttpUrlConnection和AsyncTask結合起來使用,還是比較復雜的。而2013年的Google I/O大會上,Google提出了Volley,一個提供了如下便利的HTTP庫:

  • Automatic scheduling of network requests.

  • Multiple concurrent network connections.

  • Transparent disk and memory response caching with standard HTTP cache coherence.

  • Support for request prioritization.

  • Cancellation request API. You can cancel a single request, or you can set blocks or scopes of requests to cancel.

  • Ease of customization, for example, for retry and backoff.

  • Strong ordering that makes it easy to correctly populate your UI with data fetched asynchronously from the network.

  • Debugging and tracing tools.


Volley主要架構在HttpUrlConnection之上,如果希望能夠抓取圖片或者JSON數據,Volley有自定義的抽象類型ImageRequest與JsonObjectRequest,可以自動轉化為HTTP請求。同時,Volley也有一個硬編碼的網絡連接池大小:

private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;

不過OkHttp可以自定義連接池的大小:

private int maxRequests = 64;
private int maxRequestsPerHost = 5;

executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
      new LinkedBlockingQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));

在某些情況下,OkHttp可以通過使用多線程來有更好的性能體現。不過如果現有的程序中已經用Volley做了頂層封裝,那么也可以使用HttpStack implementation這個來使用OkHttp的請求與響應接口來替換HttpUrlConnection。

到這里已經可以發現,OkHttp本質上是自定義了一套底層的網絡請求架構。目前HTTP客戶端已經逐步轉化為了支持大量圖片,特別是那種無限滾動與圖片傳輸的應用。同時,REST API已經成為了業界標準,基本上每位開發者都需要處理大量標準化的任務,類似于JSON序列化與將REST請求映射到Java的接口上。Square也在不久之后針對這兩個問題提出了自己的解決方案:

  • Retrofit - 一個類型安全的HTTP客戶端支持REST接口

  • Picasso - 針對Android的圖片下載與緩存庫

Retrofit 提供了一個面向Java代碼與REST接口之間的橋接,可以迅速將HTTP API轉化到Java接口中并且自動生成帶有完整文檔的實現:

public interface GitHubService {
    @GET("/users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
}


Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .build();

GitHubService service = retrofit.create(GitHubService.class);

除此之外,Retrofit 也支持面向JSON、XML以及Protocol Buffers的數據轉化。在另一篇博客中將AsyncTask與Volley以及Retrofit做了一個比較,其性能對比如下:

圖片加載庫對比

另一方面,Picasso是一個專門的面向圖片任務的HTTP庫。譬如,可以用一行代碼就把網絡圖片加載到ImageView中:

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

Picasso與Retrofit都是默認的使用OkHttpClient作為底層的HTTP客戶端,然而,你也可以配置自己的基于HttpUrlConnection的客戶端。

Glide是一個非常類似于Picasso的庫,不過它提供了一些額外的功能,譬如GIF動畫、簡略圖生成以及視頻。

非死book開源的它們自己的圖片加載庫Fresco使用了它們自定義的Android客戶端。其中它的一個非常優秀的特性在于Fresco的面向Bitmaps的自定義才存儲策略能夠避免JVM堆頂的垃圾回收的限制。Fresco是分配了Android中被稱為ashmem部分的內存,同時,它用了一些方法允許同時從Java以及C++代碼訪問ashmem部分,來進行NDK級別的CPU處理。為了節省數據存儲空間以及CPU的消耗,Fresco有三層不同的緩存:兩層在內存中,以及一層在內部存儲中。

到現在,我發現應該把這些網絡庫的關系表述在一張圖中。正如你可以看見的,HTTP的傳輸組件存在于示意圖的底部,與所有上層的庫進行交互。你可以選擇單純的HttpUrlConnection或者最新的OkHttpClient客戶端。

來自:http://segmentfault.com/a/1190000003965158

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!