转载请标明出处:
http://blog.csdn.net/xuehuayous/article/details/54906978
本文出自:【Kevin.zhou的博客】
Okhttp
友好地提供了异步请求,但是它的回调是在访问的当前线程。由于Android
不允许在主线程访问网络,所以返回的数据不能直接用来更新UI
,那么是否可以进行一些封装使之在子线程访问回数据调到主线程呢?
Okhttp使用回顾
使用步骤:
- 获取OkHttpClient对象
- 创建Request对象
- 创建Call对象
- 调用call对象的execute(同步)发送请求、enqueue(异步)发送请求
- 获得Response对象当中的数据
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)
的异步请求调度中把结果调到主线程中,方便以后操作。这种方式是不是有种耳目一新的感觉呢~