美文网首页
Retrofit的封装优化

Retrofit的封装优化

作者: 饭勺 | 来源:发表于2018-10-10 14:26 被阅读0次

    一、前言

    Retrofit,是针对OKhttp的二次封装,使用在网络请求方面。目前已比较普及。本文通过对Retrofit的封装,让Http请求模板化并且丰富其功能。
    

    二、功能

    封装后,功能特性如下:
    1.支持网络请求和Activity生命周期绑定;
    2.支持主动取消一个请求;
    3.支持回调函数的线程选择;
    4.支持超时时间配置
    5.支持缓存策略配置(包括离线缓存和在线缓存);
    6.支持多文件上传和图文上传。
    7.上层接口支持多种数据类型设置。
    

    三、使用介绍

    1.Http初始化,建议放在Application中:
      HttpClient.getInstance().init(context);
    

    2.Builder类介绍
      1) CommonBuilder,普通Builder
      2) LifeCycleBuilder,继承自CommonBuilder,提供生命周期管理的Builder。
      3) UploadBuilder,继承自LifeCycleBuilder,文件上传Builder.
    

    3.Http请求流程和说明
    

      1) 针对每一个url的请求,在对应的请求结果Bean中构建对应Builder。示例如下,在AccountExistResponse构建Builder
    

      public class AccountExistResponse {
    
        public String code;
        public String data;
        public String message;
    
        @Override
        public String toString() {
            return  "code = " + code +
                    "; data = " + data +
                    "; message = " + message;
        }
    
        public static class Builder extends CommonBuilder<AccountExistResponse> {
           @Override
            protected String getPath() {
                return "checkAccountIfExist";
           }
    
            @Override
            protected String getBaseUrl() {
                return "https://61.141.236.30:443/api/register/";
            }
    
            @Override
            protected String getMethod() {
                return HttpMethod.POST;
            }
        }
      }
    

      2) 构建参数并发起请求
    

        HashMap<String, String> map = new HashMap();
        map.put("phone", "15820463555");
    
        new AccountExistResponse.Builder().addBodyMap(map)
                .build(new HttpCallBack<AccountExistResponse>() {
                    @Override
                    public void onSuccess(AccountExistResponse accountExistResponse) {
                        Toast.makeText(HttpDemoActivity.this, "请求成功!  " + accountExistResponse.toString(), Toast.LENGTH_LONG).show();
                        Log.d(TAG, accountExistResponse.toString());
                    }
    
                    @Override
                    public void onError(int stateCode, String errorInfo) {
                        Toast.makeText(HttpDemoActivity.this, "请求失败!  stateCode = " + stateCode + "; errorInfo = " +errorInfo, Toast.LENGTH_LONG).show();
                        Log.d(TAG, "stateCode = " + stateCode + "; errorInfo = " +errorInfo);
                    }
                });
    

      3) 默认配置中有超时配置,自定义操作如下:
    

        HttpConfig httpConfig = HttpConfig.create(false);
        httpConfig.connectTimeout(5);
        httpConfig.readTimeout(5);
        httpConfig.writeTimeout(5);
        new RouteInfoResponse.Builder().addBodyMap(map)
                .addHeader("name", "wsh")
                .setHttpCustomConfig(httpConfig)
                .build(new HttpCallBack<RouteInfoResponse>() {
                    @Override
                    public void onSuccess(RouteInfoResponse RouteInfoResponse) {
                        Toast.makeText(HttpDemoActivity.this, "请求成功!  " + RouteInfoResponse.toString(), Toast.LENGTH_LONG).show();
                        Log.d(TAG, RouteInfoResponse.toString());
                    }
    
                    @Override
                    public void onError(int stateCode, String errorInfo) {
                        Toast.makeText(HttpDemoActivity.this, "请求失败!  stateCode = " + stateCode + "; errorInfo = " +errorInfo, Toast.LENGTH_LONG).show();
                        Log.d(TAG, "stateCode = " + stateCode + "; errorInfo = " +errorInfo);
                    }
                });
    
        a.  false 表示:无需Header中默认的配置,注意超时配置不属于Header配置; true表示保留默认的Header信息;
        b.  缓存策略也在HttpConfig中配置
    

      4) 生命周期绑定
    

        new WeatherResponse.Builder().setTag(HttpDemoActivity.this) .addParamsMap(map)
        //setTag(HttpDemoActivity.this) 即表示此请求会在HttpDemoActivity销毁时自动断开
    

      5) 回调线程设置,根据build的重载方法设置
    

        new RouteInfoResponse.Builder().addBodyMap(map)
                .addHeader("name", "wsh")
                .build(true, new HttpCallBack<RouteInfoResponse>()
        //ture 表示在主线程中返回,默认也是主线程,false即为在子线程中返回
    

      6) 主动取消请求:保存Builder,调用builder.dispose();
    

    四、主要实现流程:HttpClient类的分析

    1.HttpClient为单例
    2.提供init方法传入context
    3.提供refresh方法更新Retrofit对象以及OkHttpClient对象
    

    /**
     * @param url
     * @param headerConfig
     * @return
     */
    public HttpClient refresh(String url, HttpConfig headerConfig, HttpCallBack<T> callback) {
    
        //获取缓存Retrofit
        if (TextUtils.isEmpty(url)) {
            return null;
        }
    
        int key = getHashKey(url, headerConfig);
        mCurrentRetrofit = getCacheRetrofit(key);
        if (mCurrentRetrofit != null) {
            mCurrentServices = mCurrentRetrofit.create(HttpServices.class);
            return this;
        }
        OkHttpClient client = getOkHttpClient(url, headerConfig, callback);
        try {
            mCurrentRetrofit = new Retrofit.Builder()
                    .client(client)
                    .addConverterFactory(new ConvertFactory(mGson))
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .baseUrl(url)
                    .build();
    
        } catch (Exception e) {
            mCurrentRetrofit = null;
            e.printStackTrace();
            return null;
        }
    
        mCurrentServices = mCurrentRetrofit.create(HttpServices.class);
        putCacheRetrofit(key, mCurrentRetrofit);
        return this;
    }
    

    A.Retrofit对象针对key值有缓存,缓存大小为100;
    B.Key值的生成由url和header信息共同决定
    

    private OkHttpClient getOkHttpClient(String url, HttpConfig headerConfig, final HttpCallBack<T> callback) {
        if (mContext == null) {
            return null;
        }
        if (headerConfig == null) {
            headerConfig = HttpConfig.create(true);
    
        }
        final HttpConfig config = headerConfig;
        //缓存容量
    
        //缓存路径
        String cacheFile = mContext.getCacheDir() + "/retrofit";
        Cache cache = new Cache(new File(cacheFile), HttpConstants.SIZE_OF_CACHE);
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(config.getConnectTimeout(), TimeUnit.SECONDS)
                .readTimeout(config.getReadTimeout(), TimeUnit.SECONDS)
                .writeTimeout(config.getWriteTimeout(), TimeUnit.SECONDS)
                .addInterceptor(getHttpInterceptor(headerConfig, callback))
                .cache(cache);
    
        //测试用  跳过所有认证
        if (url.startsWith(HttpConstants.HTTPS)) {
            //SSLSocketFactory sslSocketFactory = new SslContextFactory().getSslSocket(mContext).getSocketFactory();
            //builder.sslSocketFactory(sslSocketFactory);
            builder.sslSocketFactory(new SslContextFactory().createSSLSocketFactory())
                    .hostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });
        }
    
        return builder.build();
    }
    

    A.针对Https的请求需要加载认证证书,目前没有则跳过所有认证
    

    4.通过getHttpInterceptor方法获取拦截器
    

    private Interceptor getHttpInterceptor(final HttpConfig config, final HttpCallBack<T> callback) {
        Interceptor interceptor = new Interceptor() {
            @Override
            public okhttp3.Response intercept(Chain chain) throws IOException {
                Request okHttpRequest = chain.request();
                RequestBody body = okHttpRequest.body();
                if (body != null) {
                    Log.i(TAG, "upload intercept: " + body.getClass().getSimpleName());
                    ProgressRequestBody prb = new ProgressRequestBody(body);
                    prb.setProgressListener(callback);
                    Request.Builder builder = okHttpRequest.newBuilder().method(okHttpRequest.method(), prb);
                    if (!config.getHeaders().isEmpty()) {
                        Set<Map.Entry<String, String>> entrySet = config.getHeaders().entrySet();
                        for (Map.Entry<String, String> entry : entrySet) {
                            builder.addHeader(entry.getKey(), entry.getValue());
                        }
                    }
    
                    boolean networkAvailable = NetWorkUtils.isNetworkAvailable(mContext);
                    if (networkAvailable && config.isNeedNetWorkCache()) { //有网络连接,看是否有配置,没配置,则走默认不缓存
                        CacheControl cacheControl = new CacheControl.Builder()
                                .maxAge(config.getNetWorkCacheTimeout(), TimeUnit.SECONDS)
                                .build();
    
                        builder.addHeader(HttpConstants.CACHE_CONTROL, cacheControl.toString());
                    } else if (!networkAvailable && config.isNeedNoNetWorkCache()) { //离线缓存
                        CacheControl cacheControl = new CacheControl.Builder()
                                .maxAge(config.getNoNetWorkCacheTimeout(), TimeUnit.SECONDS)
                                .build();
    
                        builder.addHeader(HttpConstants.CACHE_CONTROL, cacheControl.toString());
                    }
                    okHttpRequest = builder.build();
                }
                return chain.proceed(okHttpRequest);
            }
        };
        return interceptor;
    }
    

    A. 拦截器中增加Header信息,网络缓存配置,还有设置进度监听等操作。
    

    5.真正的请求方法doSubscribe()
    

    private void doSubscribe(final String pathKey, final int tagHash,
                             Observable<Response<String>> observable,
                             final boolean onUiCallBack, final HttpCallBack<T> callback) {
    
        Observable<Pair<String, T>> mapObservable =
                observable.map(new Function<Response<String>, Pair<String, T>>() {
            @Override
            public Pair<String, T> apply(retrofit2.Response<String> response) throws Exception {
                String msg = "";
                Pair<String, T> pair;
                if(response.isSuccessful()){
                    String data = response.body();
                    if (data != null) {
                        Class<T> cls = getParameterizedTypeClass(callback);
                        T t = mGson.fromJson(data, cls);
                        if (t != null) {
                            pair = new Pair<>(data, t);
                        } else {
                            msg = "json parse fail";
                            pair = new Pair<>(msg, null);
                        }
                        if (!onUiCallBack && t != null){ //子线程返回
                            callback.onSuccess(t);
                        }else if (!onUiCallBack && t == null){
                            callback.onError(HttpStateCode.ERROR_SUBSCRIBE_ERROR, msg);
                        }
                        return pair;
                    } else {
                        msg = "response body is null";
                    }
                }else {
                    ResponseBody errorBody = response.errorBody();
                    if (errorBody == null) {
                        msg = "errorBody is null";
                    } else {
                        msg = errorBody.string();
                    }
                }
    
                Log.e(TAG, "apply: " + msg);
                pair = new Pair<>(msg, null);
                if (!onUiCallBack){
                    callback.onError(HttpStateCode.ERROR_SUBSCRIBE_ERROR, msg);
                }
                return pair;
            }
        });
    
        mapObservable.subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Pair<String, T>>() {
    
                    @Override
                    public void onSubscribe(Disposable disposable) {
                        cacheDisposableIfNeed(disposable, tagHash, pathKey);
                    }
    
                    @Override
                    public void onNext(Pair<String, T> pair) {
                        if (!onUiCallBack) {
                            return;
                        }
                        T t = pair.second;
                        if (t != null) {
                            callback.onSuccess(t);
                        } else {
                            callback.onError(HttpStateCode.ERROR_ONNEXT_NULL, null);
                        }
                    }
    
                    @Override
                    public void onError(Throwable throwable) {
                        dispose(tagHash, pathKey);
                        if (onUiCallBack) {
                            callback.onError(HttpStateCode.ERROR_SUBSCRIBE_ERROR, throwable == null
                                    ? "" : throwable.getMessage());
                        }
                    }
    
                    @Override
                    public void onComplete() {
                        dispose(tagHash, pathKey);
                    }
                });
    }
    

    A. 将(retrofit2.Response<String> response)转化为Pair<String, T> ,T即为返回信息的java Bean类;
    B. 转为Java Bean类是从callback中的泛型获取到T的class对象,在用Gson转化
        private Class<T> getParameterizedTypeClass(Object obj) {
            ParameterizedType pt = (ParameterizedType) obj.getClass().getGenericSuperclass();
            Type[] atr = pt.getActualTypeArguments();
            if (atr != null && atr.length > 0) {
                return (Class<T>) atr[0];
            }
            return null;
        }
    C.控制返回结果的线程
    

    6.缓存Disposable以便取消请求
    

    /**
     * 缓存Disposable
     *
     * @param disposable
     * @param hash
     * @param key
     */
    private void cacheDisposableIfNeed(Disposable disposable, int hash, String key) {
        if (disposable == null) {
            return;
        }
        Pair<Integer, Disposable> pair = Pair.create(key.hashCode(), disposable);
        List<Pair<Integer, Disposable>> list = mDisposableCache.get(hash);
        if (list == null) {
            list = new ArrayList<>();
        }
        list.add(pair);
        mDisposableCache.put(hash, list);
    }
    

    7.取消请求的实现
    

    /**
     * @param tag 请求时传入的tag
     */
    public void cancel(Object tag) {
        if (tag == null) return;
        List<Pair<Integer, Disposable>> disposableList;
        disposableList = mDisposableCache.get(tag.hashCode());
        if (disposableList != null) {
            for (Pair<Integer, Disposable> pair : disposableList) {
                pair.second.dispose();
            }
            mDisposableCache.remove(tag.hashCode());
        }
    }
    
    /**
     *
     * @param hash
     * @param key
     * @return 返回是否成功删除
     */
    public boolean dispose(int hash, @NonNull String key) {
        List<Pair<Integer, Disposable>> list = mDisposableCache.get(hash);
        Pair<Integer, Disposable> removePair = null;
        if (list != null) {
            for (Pair<Integer, Disposable> pair : list) {
                if (key.hashCode() == pair.first) {
                    pair.second.dispose();
                    removePair = pair;
                    break;
                }
            }
        }
        if (list != null && removePair != null) {
            list.remove(removePair);
            return true;
        }
        return false;
    }
    

    8.绑定生命周期
    

    public class HttpClient<T> implements GenericLifecycleObserver {
        ......
        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
            if (source.getLifecycle().getCurrentState() == Lifecycle.State.DESTROYED) {
                source.getLifecycle().removeObserver(this);
                cancel(source);
            }
        }
        ......
    }
    

    至此,整个封装思路便讲完了。以下是源码路径:
    

    源码及Demo地址点这里

    相关文章

      网友评论

          本文标题:Retrofit的封装优化

          本文链接:https://www.haomeiwen.com/subject/onasaftx.html