@SmartDengg
2015-12-23T08:38:44.000000Z
字数 4732
阅读 1332
- 原文链接: Using OkHttp
- 原文出处: Codepath
- 译者: 小鄧子
- 状态: 完成
OkHttp是Square公司出品的一个三方类库,用于发送和接受基于HTTP的网络请求。它基于Okio,一个试图通过创建共享内存池的方式处理数据的读和写,比标准的Java I/O类库更高效的类库。它也是Retrofit的底层类库,提供类型安全的基于REST的API请求。
OkHttp实际上实现了HttpUrlConnection,并且在Android 4.4及以后版本的源码中已经使用Okhttp代替HttpUrlConnection了。因此当我们按照这个章节的指导进行操作的时候,HttpUrlConnection类可能来自于OkHttp类库。然而,OkHttp提供独立的API,将发送和接收网络请求变得更简单,这正是这篇文章所要描述的。
确保AndroidManifest.xml文件中能够得到网络权限。
<uses-permission android:name="android.permission.INTERNET"/>
然后,仅仅将这一行添加到app/build.gradle文件中
compile 'com.squareup.okhttp:okhttp:2.7.0'
首先,我们必须初始化一个OkHttpClient实例,然后创建一个Request
对象。
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
如果需要添加一些请求参数,可以通过OkHttp提供的HttpUrl
类构造该URL:
HttpUrl.Builder urlBuilder = HttpUrl.parse("https://ajax.googleapis.com/ajax/services/search/images")
.newBuilder();
urlBuilder.addQueryParameter("v", "1.0");
urlBuilder.addQueryParameter("q", "android");
urlBuilder.addQueryParameter("rsz", "8");
String url = urlBuilder.build().toString();
Request request = new Request.Builder()
.url(url)
.build();
如果需要Authenticated作为参数,也可以添加到请求头中:
Request request = new Request.Builder()
.header("Authorization", "token abcd")
.url("https://api.github.com/users/codepath")
.build();
我们可以创建一个call
对象,然后同步调度网络请求:
Response response = client.newCall(request).execute();
因为Android不允许在主线程处理请求网络,所以这种同步调用方式,只能在独立的工作线程或者后台Service中调用。你也可以使用AsyncTask处理轻量级网络操作。
我们也可以通过创建call
来处理异步网络请求,使用enqueue()
方法,然后传入一个匿名Callback
,并实现onFailure()
和onResponse()
方法。
// Get a handler that can be used to post to the main thread
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(final Response response) throws IOException {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
}
}
这种场景下OkHttp会创建一个新的工作线程来调度网络请求,然后在该线程中回调Response。它主要是一个Java库,所以不要在这个方法里面做Android Framework中明令禁止的事情,比如不允许在非UI线程中更新View。如果你需要更新任何View,可以使用runOnUiThread()
或者将结果发送到主线程。点击这个指南可以了解更多。
假设请求没有被取消并且网络连接也毫无问题,onResponse()
将会被回调。它传递一个Response
对象,可以通过它来检查Status Code
、Response Body
、Header
或者所有返回的数据。例如可以通过调用isSuccessful()
来判断返回的状态码是否为2xx(比如:200,201等)。
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
响应头也可以通过集合的形式获取:
Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
Log.d("DEBUG", responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
响应头也可以使用response.header()
直接获取:
String header = response.header("Date");
我们也可以通过调用response.body()
获得响应数据,然后调用string()
读取整个Body的原始数据。值得一提的是,response.body()
只能运行一次,并且应该在后台线程中完成操作。
Log.d("DEBUG", response.body().string());
假设我们访问了Github API,并返回了一串Json数据。
Request request = new Request.Builder()
.url("https://api.github.com/users/codepath")
.build();
我们可以通过Decode将响应结果转换成JSONObject
或者JSONArray
:
client.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(final Response response) throws IOException {
try {
String responseData = response.body().string();
JSONObject json = new JSONObject(responseData);
final String owner = json.getString("name");
} catch (JSONException e) {
}
}
});
需要注意的是,response.body().string()
会将整个数据加载到内存中。为了更有效的使用内存,建议使用charStream()
方法将response
转换成Stream,然后进行处理。然而这种方法需要使用Gson库。详情请阅读这篇使用说明。
使用Gson,必须首先定义一个与Json Response有直接映射关系的类。(译者注:Gson解析通过反射完成,不需要额外定义setter方法):
static class GitUser {
String name;
String url;
int id;
}
Gson可以将数据直接转换成Java Model:
// Create new gson object
final Gson gson = new Gson();
// Get a handler that can be used to post to the main thread
client.newCall(request).enqueue(new Callback() {
// Parse response using gson deserializer
@Override
public void onResponse(final Response response) throws IOException {
// Process the data on the worker thread
GitUser user = gson.fromJson(response.body().charStream(), GitUser.class);
// Access deserialized user object here
}
}
OkHttp有个机制,它允许使用 interceptors(拦截器)修改出站请求。一个常见的用例就是OAuth Protocol(开放协议),它需要使用私钥签名的网络请求。在OkHttp signpost library中使用了SignPost library,然后通过interceptor标记每一个出站请求。这样调用者就不需要记住每一个请求,省去了重复标记的步骤:
OkHttpOAuthConsumer consumer = new OkHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
consumer.setTokenWithSecret(token, secret);
okHttpClient.interceptors().add(new SigningInterceptor(consumer));
阅读Square的官方指导recipe guide以及其他使用OkHttp的场景。
Android’s HTTP Clients - Android Developers blog, September 29, 2011
http://android-developers.blogspot.com/2011/09/androids-http-clients.html
https://speakerdeck.com/jakewharton/a-few-ok-libraries-droidcon-mtl-2015
http://stackoverflow.com/questions/24246783/okhttp-response-callbacks-on-the-main-thread