美文网首页Http
OKHttp全解析系列(八) --Retrofit+RxJava

OKHttp全解析系列(八) --Retrofit+RxJava

作者: 嘎啦果安卓兽 | 来源:发表于2017-11-08 17:56 被阅读173次

    Retrofit+RxJava+OKHttp接入指南

    前面学习了Retrofit和OKHttp,就尝试在自己的项目中使用了一下。本文介绍了接入Retrofit的一些问题,可供想使用Retrofit的同学作为参考。

    api请求添加公共的Headers和params

    如果通过注解的方式,在每个请求接口上添加公共headers和params,代码会太繁琐和冗余。所以可通过拦截器添加共的headers和params。

    我定义的拦截器CommonInterceptor的拦截方法如下:

     @Override
    public Response intercept(Interceptor.Chain chain) throws IOException {
            Request oldRequest = chain.request();
    
            // 添加公共的请求参数
            HttpUrl.Builder authorizedUrlBuilder = oldRequest.url()
                    .newBuilder()
                    .scheme(oldRequest.url().scheme())
                    .host(oldRequest.url().host())
                    .addQueryParameter("from", "android")
                    .addQueryParameter("version", Config.SDK_VERSION);
            // 添加公共的请求headers
            Request newRequest = oldRequest.newBuilder()
                    .addHeader("User-Agent", "android_" + Config.SDK_VERSION +
                            ";" + Config.UNICOM_USERAGENT)
                    .method(oldRequest.method(), oldRequest.body())
                    .url(authorizedUrlBuilder.build())
                    .build();
    
            return chain.proceed(newRequest);
    }
    
    

    把拦截器通过OKHttpClent设置给Retrofit:

    OkHttpClient client = new OkHttpClient.Builder()
                    .addInterceptor(commonInterceptor)
                    .build();
    retrofit = new Retrofit.Builder()
                .baseUrl(WebConfig.ONLINE_URL_PRE)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .client(client)
                .build();
    
    

    查看请求和响应Log日志

    使用Retrofit查看请求和响应日志也是通过拦截器实现,HttpLoggingInterceptor是OKHttp提供的Log拦截器可以直接使用:

    @Override
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    //设置日志级别       
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);
    
    

    把拦截器通过OKHttpClent设置给Retrofit,同上不赘述。

    使用OKHttp数据缓存功能

    OKHttp本身就支持数据缓存功能,只要在请求接口定义缓存标签:

      /**
      * 获取鉴权接口
      */
      @Headers("Cache-Control:public ,max-age=2592000")
      @GET("oauth/token")
      Observable<Oauth.Result> getToken();
    
    

    但有个问题,如果服务端不支持缓存,会返回一些干扰信息,会影响缓存功能,所以要使用拦截器清除干扰信息,拦截方法如下:

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = chain.proceed(request);
        if (NetworkUtils.isNetworkConnected(PlayerManager.getAppContext())) {
            String cacheControl = request.cacheControl().toString();
            return response.newBuilder()
                   //清除头信息
                   .removeHeader("Pragma")
                   .header("Cache-Control", cacheControl)
                   .build();
         }
         return response;
    }
    
    

    把拦截器通过OKHttpClent设置给Retrofit:

    CacheInterceptor cacheInterceptor = new CacheInterceptor();
    //设置缓存目录、缓存大小
    File httpCacheDirectory = new File(EnvironmentUtilities.getHttpCachePath());
            int cacheSize = 10 * 1024 * 1024;
    Cache cache = new Cache(httpCacheDirectory, cacheSize);
    
    OkHttpClient client = new OkHttpClient.Builder()
                    .addNetworkInterceptor(cacheInterceptor)
                    .cache(cache)
                    .build();
    ......
    
    

    提示:该缓存是api级别的对数据整体缓存,整读整存。如果缓存数据跟业务管理密切需要部分更新数据,需要自己用数据库实现缓存功能。

    统一处理网络请求状态、服务端返回状态码ErrorCode

    每个接口在处理业务数据之前,都要处理网络请求状态、服务端返回状态码ErrorCode,这应该一个统一的地方处理。

    构建DefaultObserver处理服务器响应数据。定义DefaultObserver类继承Observer,并重写相应的方法。
    1、在onError()方法里

    @Override
    public void onError(Throwable e) {
            LogUtils.e("Retrofit", e.getMessage());
            mBaseImpl.dismissProgress();
            // HTTP错误
            if (e instanceof HttpException) {     
                onException(ExceptionReason.BAD_NETWORK);
            // 连接错误
            } else if (e instanceof ConnectException
                    || e instanceof UnknownHostException) {   
                onException(CONNECT_ERROR);
            // 连接超时
            } else if (e instanceof InterruptedIOException) {   
                onException(CONNECT_TIMEOUT);
            // 解析错误
            } else if (e instanceof JsonParseException
                    || e instanceof JSONException
                    || e instanceof ParseException) { 
                onException(PARSE_ERROR);
            } else {
                onException(UNKNOWN_ERROR);
            }
     }
    

    onException方法中做各种异常处理,比如Toast提示。

    2、在onNext统一处理服务器返回状态码

    @Override
    public void onNext(T response) {
            mBaseImpl.dismissProgress();
            if (!response.isError()) {
                onSuccess(response);
            } else {
                onFail(response);
            }
    }
    

    onFail方法处理,异常返回码的各种状态码,onSuccess为抽象方法,用户实现观察者时候实现该方法,真正的处理正常返回的业务数据。

    Rxjava生命周期管理

    异步处理,如果控制不好生命周期,会导致内存泄露和一些莫名其妙的问题。
    封装BaseActivityRxjava生命周期管理。

    public class BaseRxActivity extends AppCompatActivity implements BaseImpl {
        private CustomProgressDialog mProgressDialog;
        private CompositeDisposable disposables2Stop;// 管理Stop取消订阅者者
        private CompositeDisposable disposables2Destroy;// 管理Destroy取消订阅者者
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            if (disposables2Destroy != null) {
                throw new IllegalStateException("onCreate called multiple times");
            }
            disposables2Destroy = new CompositeDisposable();
    
        }
    
        public boolean addRxStop(Disposable disposable) {
            if (disposables2Stop == null) {
                throw new IllegalStateException(
                        "addUtilStop should be called between onStart and onStop");
            }
            disposables2Stop.add(disposable);
            return true;
        }
    
        public boolean addRxDestroy(Disposable disposable) {
            if (disposables2Destroy == null) {
                throw new IllegalStateException(
                        "addUtilDestroy should be called between onCreate and onDestroy");
            }
            disposables2Destroy.add(disposable);
            return true;
        }
    
        public void remove(Disposable disposable) {
            if (disposables2Stop == null && disposables2Destroy == null) {
                throw new IllegalStateException("remove should not be called after onDestroy");
            }
            if (disposables2Stop != null) {
                disposables2Stop.remove(disposable);
            }
            if (disposables2Destroy != null) {
                disposables2Destroy.remove(disposable);
            }
        }
    
        public void onStart() {
            super.onStart();
            if (disposables2Stop != null) {
                throw new IllegalStateException("onStart called multiple times");
            }
            disposables2Stop = new CompositeDisposable();
        }
    
        public void onStop() {
            super.onStop();
            if (disposables2Stop == null) {
                throw new IllegalStateException("onStop called multiple times or onStart not called");
            }
            disposables2Stop.dispose();
            disposables2Stop = null;
        }
    
        public void onDestroy() {
            super.onDestroy();
            if (disposables2Destroy == null) {
                throw new IllegalStateException(
                        "onDestroy called multiple times or onCreate not called");
            }
            disposables2Destroy.dispose();
            disposables2Destroy = null;
        }
    
    }
    
    

    生命周期管理是通过任务管理器CompositeDisposable 来管理的,就是在执行请求时候将Disposable添加到一个CompositeDisposable ,在页面至后台或者销毁时候调用dispose,取消Rxjava生命周期。

    Disposable添加是在Observer 的onSubscribe调用的

    @Override
    public void onSubscribe(Disposable d) {
            //  在onStop中取消订阅
            if (isAddInStop) {    
                mBaseImpl.addRxStop(d);
            //  在onDestroy中取消订阅    
            } else { 
                mBaseImpl.addRxDestroy(d);
            }
     }
    
    

    自定义Convert响应数据转换器

    一般Retrofit会配套使用Gson使用,但是如果改造老项目,把json解析从手动解析换成Gson解析可能工作量巨大,还是个容易出错的体力话。特别的接口协议设计不规范,没有面相对象的思想的时候,你会改到想吐。所以这种办法是自定义Convert数据转换器,复用原来的json数据解析。

    @Override
    public T convert(ResponseBody responseBody) throws IOException {
            T t;
            try {
                t = ReflectUtil.getTClassInstance(type);
                t.parse(responseBody.string());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                return null;
            } catch (InstantiationException e) {
                e.printStackTrace();
                return null;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                return null;
            }
            return t;
    }
    
    parse方法是我们项目自己手动解析的方法。
    
    

    Retrofit直接请求完整的Url

    我们老项目中,没有使用Param来组装请求,而是直接用字符串拼装完整的url,然后请求api。所以去改造成params的话,又是一个体力话,所以我们这种直接使用完整的url请求。幸好Retrofit提供了这样的标签。

    
    @GET()
    Observable<ResponseBody> getFile(@Url String url);
    
    

    这种情况还适用于,url是个动态的地址,比如是服务端返回的地址。

    OKHttp链接Https不校验身份

    跟HttpcCient和HttpURLConnection一样需要重写SSLSocketFactory绕过ssl身份验证,重写HostNameVerifier并且不做主机名验证。

    //自定义TrustManager,绕过ssl身份验证
    TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
        @Override
        public void checkClientTrusted(
                java.security.cert.X509Certificate[] x509Certificates,
                String s) throws java.security.cert.CertificateException {
        }
    
        @Override
        public void checkServerTrusted(
                java.security.cert.X509Certificate[] x509Certificates,
                String s) throws java.security.cert.CertificateException {
        }
    
        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return new java.security.cert.X509Certificate[]{};
        }
    }};
    SSLContext sc = null;
    try {
        sc = SSLContext.getInstance("TLS");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (KeyManagementException e) {
        e.printStackTrace();
    }
    
    OkHttpClient client = new OkHttpClient.Builder()
             //重写HostNameVerifier,主机名验证直接返回true
            .hostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            })
            .sslSocketFactory(sc.getSocketFactory())
            .build();
    
    

    以上为我在接入Retrofit和OKHttp遇到的问题以及解决方案,大家还有其他问题可以留言,大家探讨后我再补充。

    相关文章

      网友评论

        本文标题:OKHttp全解析系列(八) --Retrofit+RxJava

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