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 提供了一個面向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