网络请求

Introduction

笔者没有把 OkHttp 归纳到 Android 体系内,还是放置到了 Java 体系内。Retrofit 本身就是对于 OkHttp 的封装。

Comparison( 比较 )

目前基本上每个应用都会使用 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 IO 大会上,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 做了一个比较,其性能对比如下:

上一页