转载请标明出处:
http://blog.csdn.net/xuehuayous/article/details/54906978
本文出自:【Kevin.zhou的博客】
Okhttp
友好地提供了异步请求,但是它的回调是在访问的当前线程。由于Android
不允许在主线程访问网络,所以返回的数据不能直接用来更新UI
,那么是否可以进行一些封装使之在子线程访问回数据调到主线程呢?
OK按照使用步骤进行简单的使用
// 1. 获取OkHttpClient对象
OkHttpClient client = new OkHttpClient();
// 2. 创建Request对象
Request request = new Request.Builder()
.url("http://123.57.31.11/androidnet/getJoke?id=7")
.get()
.build();
// 3. 创建Call对象
Call call = client.newCall(request);
// 4. 调用call对象的execute(同步)发送请求、enqueue(异步)发送请求
Log.d(TAG, "Thread id->" + Thread.currentThread().getId());
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, "Thread id->" + Thread.currentThread().getId());
// 5. 获得Response对象当中的数据
String responseStr = response.body().string();
Log.d(TAG, "responseStr: " + responseStr);
}
});
Log
信息如下:
D/MainActivity: Thread id->1
D/MainActivity: Thread id->162
D/MainActivity: responseStr: 女神今天过生日,刚才问我晚上有没有空,我心中暗喜难道她是要邀请我一起过生日,于是故作镇定的说:“有啊,怎么了?”然后她说:“太好了,今晚我生日聚会,很多朋友一起喝酒,你吃完晚饭来帮我们开车吧!”
可以看到创建请求的过程是在UI
完成的,加入请求调度进行异步请求的返回结果是在非UI
线程的。
通过对Okhttp使用步骤的分解,是Call
进行的异步请求调度的过程中由主线程转到子线程的,那么就要对Call
进行封装。
可以看到Okhttp
的 Call
为一个接口,其实在创建Call
对象的时候是创建的Call
接口的实现类RealCall
,OkHttpClient
中newCall
方法如下:
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
那么我们就可以定义一个自己的Call
,然后对okhttp3.Call
进行包装,在enqueue(Callback callback)
中进行修改,把结果调到主线程就可以啦~
创建一个自己的OkHttpCall
继承okhttp3.Call
接口:
/**
* Created by zhouwenkai on 2017/2/6.
*/
public class OkHttpCall implements okhttp3.Call {
}
实现抽象方法
/**
* Created by zhouwenkai on 2017/2/6.
*/
public class OkHttpCall implements okhttp3.Call {
@Override
public Request request() {
return null;
}
@Override
public Response execute() throws IOException {
return null;
}
@Override
public void enqueue(Callback callback) {
}
@Override
public void cancel() {
}
@Override
public boolean isExecuted() {
return false;
}
@Override
public boolean isCanceled() {
return false;
}
@Override
public okhttp3.Call clone() {
return null;
}
}
注入Call
由于是对Call
进行包装,所以这里在构造方法中注入Call
/**
* Created by zhouwenkai on 2017/2/6.
*/
public class OkHttpCall implements okhttp3.Call {
okhttp3.Call realCall;
public OkHttpCall(okhttp3.Call call) {
this.realCall = call;
}
// ... ...
}
所有方法都交给Call
去处理
/**
* Created by zhouwenkai on 2017/2/6.
*/
public class OkHttpCall implements okhttp3.Call {
okhttp3.Call realCall;
public OkHttpCall(okhttp3.Call call) {
this.realCall = call;
}
@Override
public Request request() {
return realCall.request();
}
@Override
public Response execute() throws IOException {
return realCall.execute();
}
@Override
public void enqueue(Callback callback) {
// TODO 大干一场
}
@Override
public void cancel() {
realCall.cancel();
}
@Override
public boolean isExecuted() {
return realCall.isExecuted();
}
@Override
public boolean isCanceled() {
return realCall.isCanceled();
}
@Override
public okhttp3.Call clone() {
return realCall.clone();
}
}
enqueue
方法编写
@Override
public void enqueue(final Callback callback) {
realCall.enqueue(new Callback() {
@Override
public void onFailure(final Call call, final IOException e) {
// TODO 访问失败回调到主线程
}
@Override
public void onResponse(final Call call, final Response response) throws IOException {
// TODO 访问成功回调到主线程
}
});
}
费了那么大工夫,最主要的就是上面代码的两个TODO
即怎样把访问结果回调到主线程去。
创建Handler
帮助类
/**
* Created by zhouwenkai on 2017/2/6.
*/
public class MainThreadExecutor implements Executor {
private Handler handler = new Handler(Looper.getMainLooper());
private MainThreadExecutor() {
}
private static MainThreadExecutor sInstance = null;
public static MainThreadExecutor getInstance() {
if (sInstance == null) {
synchronized (MainThreadExecutor.class) {
if (sInstance == null)
sInstance = new MainThreadExecutor();
}
}
return sInstance;
}
@Override
public void execute(Runnable command) {
handler.post(command);
}
}
这里主要是创建了一个Handler
然后就可以调度到主线程啦~
访问结果回调到主线程
@Override
public void enqueue(final Callback callback) {
realCall.enqueue(new Callback() {
@Override
public void onFailure(final Call call, final IOException e) {
MainThreadExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
callback.onFailure(call, e);
}
});
}
@Override
public void onResponse(final Call call, final Response response) throws IOException {
MainThreadExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
try {
callback.onResponse(call, response);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
});
}
OK,自此封装就完成了,下面为完整代码以及如何使用。
OkHttpCall
package com.kevin.okhttp;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
/**
* Created by zhouwenkai on 2017/2/6.
*/
public class OkHttpCall implements okhttp3.Call {
okhttp3.Call realCall;
public OkHttpCall(okhttp3.Call call) {
this.realCall = call;
}
@Override
public Request request() {
return realCall.request();
}
@Override
public Response execute() throws IOException {
return realCall.execute();
}
@Override
public void enqueue(final Callback callback) {
realCall.enqueue(new Callback() {
@Override
public void onFailure(final Call call, final IOException e) {
MainThreadExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
callback.onFailure(call, e);
}
});
}
@Override
public void onResponse(final Call call, final Response response) throws IOException {
MainThreadExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
try {
callback.onResponse(call, response);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
});
}
@Override
public void cancel() {
realCall.cancel();
}
@Override
public boolean isExecuted() {
return realCall.isExecuted();
}
@Override
public boolean isCanceled() {
return realCall.isCanceled();
}
@Override
public okhttp3.Call clone() {
return realCall.clone();
}
}
MainThreadExecutor
package com.kevin.okhttp;
import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.Executor;
/**
* Created by zhouwenkai on 2017/2/6.
*/
public class MainThreadExecutor implements Executor {
private Handler handler = new Handler(Looper.getMainLooper());
private MainThreadExecutor() {
}
private static MainThreadExecutor sInstance = null;
public static MainThreadExecutor getInstance() {
if (sInstance == null) {
synchronized (MainThreadExecutor.class) {
if (sInstance == null)
sInstance = new MainThreadExecutor();
}
}
return sInstance;
}
@Override
public void execute(Runnable command) {
handler.post(command);
}
}
和我们之前的使用方式基本一致,只是对Call
加了层包装,多说无益,上代码:
// 1. 获取OkHttpClient对象
OkHttpClient client = new OkHttpClient();
// 2. 创建Request对象
Request request = new Request.Builder()
.url("http://123.57.31.11/androidnet/getJoke?id=7")
.get()
.build();
// 3. 创建Call对象
Call call = client.newCall(request);
// 4. 使用OkHttpCall对Call进行包装
OkHttpCall okhttpCall = new OkHttpCall(call);
// 5. 调用OkHttpCall对象的enqueue(异步)发送请求
okhttpCall.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// 6. 获得Response对象当中的数据
String responseStr = response.body().string();
Log.d(TAG, "responseStr: " + responseStr);
}
});
其实就是我们开始写的使用方法,只不过修改了如下两行代码:
// 4. 使用OkHttpCall对Call进行包装
OkHttpCall okHhttpCall = new OkHttpCall(call);
// 5. 调用OkHttpCall对象的enqueue(异步)发送请求
okHttpCall.enqueue(new Callback() {
// ... ...
}
以上就实现了对okhttp3.Call
的封装,我们自定义了一个OkHttpCall
作为okhttp3.Call
的包装类,在enqueue(Callback callback)
的异步请求调度中把结果调到主线程中,方便以后操作。这种方式是不是有种耳目一新的感觉呢~