网络请求工具类之OkHttp3封装(二)上(支持请求取消、异步请求的线程切换)
2016-06-24 15:05
429 查看
上一篇封装的已经满足一些网络请求的基本场景,但没有将网络请求与Activity/Fragment的生命周期进行绑定,导致切换页面时没有及时释放网络请求的相关资源;其次OkHttp3的异步请求结束后的回调方法是在子线程中,若要进行UI操作就得采用 runOnUiThread
方法进一步包裹,程序显的很笨拙,总之用起来不是很爽,那这一篇就对上面两个方面来进行优化。
优化任务:
1、网络请求与Activity/Fragment的生命周期进行绑定,做到Activity/Fragment销毁时,与其绑定的网络请求全部取消以释放资源
2、异步请求采用UI线程回调方式
对于任务1,实现比较简单,采用Map集合保存每次网络请求Call,Map的键为Activity/Fragment,值采用List集合保存该页面下所有的Call,考虑到多线程情况的复杂性和不可预见性,这里采用了并发包下的ConcurrentHashMap
。
Activity/Fragment中直接进行网络请求,代码如下:
HttpInfo构建扩展了一个新方法build(this),将当前页面设置为一个tag标记进行请求的绑定。在OkHttpUtil工具类中增加以下两个方法:
这样就完成了请求的绑定、保存与取消等操作,但问题来了,取消请求的方法在什么时候调用呢?答案当然是Activity/Fragment销毁时调用。下面需要编写一个Activity生命周期的回调接口,并注册到App的Application中,首先在Application中注册,代码如下
在OkHttpUtil中添加以下代码:
大家应该注意到 cancelCall(activity.getClass());方法的调用。整个逻辑贯通起来,每次请求调用putCall方法,在销毁时调用cancelCall方法,这样就完成了请求与Activity/Fragment生命周期绑定的功能了。
任务2,异步请求采用UI线程回调方式将在下面一篇文章中来叙述下。
最后贴一下HttpInfo的代码(增加了请求标识tag):
项目已上传至GitHub:https://github.com/MrZhousf/OkHttp3
方法进一步包裹,程序显的很笨拙,总之用起来不是很爽,那这一篇就对上面两个方面来进行优化。
优化任务:
1、网络请求与Activity/Fragment的生命周期进行绑定,做到Activity/Fragment销毁时,与其绑定的网络请求全部取消以释放资源
2、异步请求采用UI线程回调方式
对于任务1,实现比较简单,采用Map集合保存每次网络请求Call,Map的键为Activity/Fragment,值采用List集合保存该页面下所有的Call,考虑到多线程情况的复杂性和不可预见性,这里采用了并发包下的ConcurrentHashMap
。
/** * 请求集合: key=Activity value=Call集合 */ private static Map<Class<?>,List<Call>> callsMap = new ConcurrentHashMap<Class<?>,List<Call>>();
Activity/Fragment中直接进行网络请求,代码如下:
private void doHttpAsync(){ HttpInfo info = HttpInfo.Builder().setUrl(url).build(this); OkHttpUtil.Builder() .setCacheLevel(OkHttpUtil.CacheLevel.FIRST_LEVEL).build() .doGetAsync(info, new OkHttpUtil.CallbackOk() { @Override public void onResponse(HttpInfo info) throws IOException { if(info.isSuccessful()){ InfoBean bean = info.getRetDetail(InfoBean.class); // You can operate the UI directly. } } }); }
HttpInfo构建扩展了一个新方法build(this),将当前页面设置为一个tag标记进行请求的绑定。在OkHttpUtil工具类中增加以下两个方法:
/** * 保存请求集合 * @param info * @param call */ private void putCall(HttpInfo info, Call call){ if(null != info.getTag()){ List<Call> callList = callsMap.get(info.getTag()); if(null == callList){ callList = new LinkedList<Call>(); callList.add(call); callsMap.put(info.getTag(),callList); }else{ callList.add(call); } } }
/** * 取消请求 * @param clazz */ public static void cancelCall(Class<?> clazz){ List<Call> callList = callsMap.get(clazz); if(null != callList){ for(Call call : callList){ if(!call.isCanceled()) call.cancel(); } callsMap.remove(clazz); } }
这样就完成了请求的绑定、保存与取消等操作,但问题来了,取消请求的方法在什么时候调用呢?答案当然是Activity/Fragment销毁时调用。下面需要编写一个Activity生命周期的回调接口,并注册到App的Application中,首先在Application中注册,代码如下
private OkHttpUtil.BaseActivityLifecycleCallbacks activityLifecycleCallbacks; @Override public void onCreate() { super.onCreate(); registerActivityLifecycleCallbacks(activityLifecycleCallbacks = new OkHttpUtil.BaseActivityLifecycleCallbacks()); }
在OkHttpUtil中添加以下代码:
/** * Activity声明周期回调 */ public static class BaseActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { } @Override public void onActivityStarted(Activity activity) { } @Override public void onActivityResumed(Activity activity) { } @Override public void onActivityPaused(Activity activity) { } @Override public void onActivityStopped(Activity activity) { } @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { } @Override public void onActivityDestroyed(Activity activity) { cancelCall(activity.getClass()); } }
大家应该注意到 cancelCall(activity.getClass());方法的调用。整个逻辑贯通起来,每次请求调用putCall方法,在销毁时调用cancelCall方法,这样就完成了请求与Activity/Fragment生命周期绑定的功能了。
任务2,异步请求采用UI线程回调方式将在下面一篇文章中来叙述下。
最后贴一下HttpInfo的代码(增加了请求标识tag):
package okHttp; import android.app.Activity; import android.support.v4.app.Fragment; import android.text.TextUtils; import com.google.gson.Gson; import java.util.HashMap; import java.util.Map; public class HttpInfo { //**请求参数定义**/ private String url;//请求地址 private Map<String,String> params;//请求参数 //**响应返回参数定义**/ private int retCode;//返回码 private String retDetail;//返回结果 protected Class<?> tag; public HttpInfo(Builder builder) { this.url = builder.url; this.params = builder.params; this.tag = builder.tag; } public static Builder Builder() { return new Builder(); } public static final class Builder { private String url; private Map<String,String> params; private Class<?> tag; public Builder() { } public HttpInfo build(){ return new HttpInfo(this); } public HttpInfo build(Object object){ setTag(object); return new HttpInfo(this); } public Builder setUrl(String url) { this.url = url; return this; } public Builder addParams(Map<String, String> params) { this.params = params; return this; } public Builder addParam(String key,String value){ if(null == this.params) this.params = new HashMap<String,String>(); if(!TextUtils.isEmpty(key)) this.params.put(key,value); return this; } public Builder setTag(Object object) { if(object instanceof Activity){ Activity activity = (Activity) object; this.tag = activity.getClass(); } if(object instanceof Fragment){ Fragment fragment = (Fragment) object; this.tag = fragment.getActivity().getClass(); } return this; } } //**请求返回常量定义**/ public final int NonNetwork = 1; private final String NonNetwork_Detail = "网络中断"; public final int SUCCESS = 2; private final String SUCCESS_Detail = "发送请求成功"; public final int ProtocolException = 3; private final String ProtocolException_Detail = "请检查协议类型是否正确"; public final int NoResult = 4; private final String NoResult_Detail = "无法获取返回信息(服务器内部错误)"; public final int CheckURL = 5; private final String CheckURL_Detail = "请检查请求地址是否正确"; public final int CheckNet = 6; private final String CheckNet_Detail = "请检查网络连接是否正常"; public final int ConnectionTimeOut = 7; private final String ConnectionTimeOut_Detail = "连接超时"; public final int WriteAndReadTimeOut = 8; private final String WriteAndReadTimeOut_Detail = "读写超时"; public HttpInfo packInfo(int retCode){ return packInfo(retCode, null); } public HttpInfo packInfo(int retCode, String retDetail){ this.retCode = retCode; switch (retCode){ case NonNetwork: this.retDetail = NonNetwork_Detail; break; case SUCCESS: this.retDetail = SUCCESS_Detail; break; case ProtocolException: this.retDetail = ProtocolException_Detail; break; case NoResult: this.retDetail = NoResult_Detail; break; case CheckURL: this.retDetail = CheckURL_Detail; break; case CheckNet: this.retDetail = CheckNet_Detail; break; case ConnectionTimeOut: this.retDetail = ConnectionTimeOut_Detail; break; case WriteAndReadTimeOut: this.retDetail = WriteAndReadTimeOut_Detail; break; } if(!TextUtils.isEmpty(retDetail)){ this.retDetail = retDetail; } return this; } public boolean isSuccessful(){ if(this.retCode == SUCCESS) return true; return false; } public String getUrl() { return url; } public String getRetDetail() { return retDetail; } public <T> T getRetDetail(Class<T> clazz){ return new Gson().fromJson(retDetail, clazz); } public void setRetDetail(String retDetail) { this.retDetail = retDetail; } public Map<String, String> getParams() { return params; } public Class<?> getTag() { return tag; } }
项目已上传至GitHub:https://github.com/MrZhousf/OkHttp3
相关文章推荐
- 请求头和响应头的作用和内容
- 本地时间不对引起的https网页提示证书风险
- 苹果App部署HTTPS进行在线下载安装
- Ubuntu/Linux网络配置命令
- 用开源工具Xplico助力网络应用层数据解码
- 用开源工具Xplico助力网络应用层数据解码
- HttpClient:Target host must not be null, or set in parameters
- CNN卷积神经网络
- lampp下配置https,并设置cookie跨域
- URL 长度,POST 数据长度及 COOKIE 长度限制
- Okhttp使用详解
- haproxy 看到的是https,后台是http的原因
- haproxy 看到的是https,后台是http的原因
- haproxy 看到的是https,后台是http的原因
- 获取网络同步时间
- frontend http 前端名字定义问题
- frontend http 前端名字定义问题
- frontend http 前端名字定义问题
- Android studio开发找不到HttpClient问题
- 机器学习之DBN(Deep Belief Network,深度信念网络)