网络请求工具类之OkHttp3封装(二)上(支持请求取消、异步请求的线程切换)

上一篇封装的已经满足一些网络请求的基本场景,但没有将网络请求与Activity/Fragment的生命周期进行绑定,导致切换页面时没有及时释放网络请求的相关资源;其次OkHttp3的异步请求结束后的回调方法是在子线程中,若要进行UI操作就得采用 runOnUiThread 方法进一步包裹,程序显的很笨拙,总之用起来不是很爽,那这一篇就对上面两个方面来进行优化。

优化任务:

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


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值