Okhttp3+Rxjava+Retrofit2封装

一、简介

Okhttp3+Rxjava+Retrofit2是目前在android中比较流行的一个网络框架,想起以前最开始用android原生Httpcient、URLConnection,随后使用的是Volley、Okhttp。网络框架一直在演变和升级。

今天我分享的是自己封装的Okhttp3+Rxjava+Retrofit2,其中主要功能如下:日志拦截器、Header加密(Token)、缓存策略、生命周期管理、网络加载弹窗等功能。

GitHub项目地址:https://github.com/hjy15759633702/TestHttp

以下是封装后代码:

/**
 * 登录请求
 * @author hjy
 * created at 2017/12/10 11:51
 */
private void login() {
        Map params = new HashMap<>();
        params.put("account","test");
        params.put("password","test");
        Observable> observable = HttpManager.init().with(MainActivity.this).login(params);
        observable.compose(RxSchedulersHelper.>doIoAndMain())
                .compose(RxDataHelper.handResult(LifeCycleEvent.DESTROY,lifecycleSubject))
                .subscribe(new RxSubscriber(MainActivity.this) {
                    @Override
                    protected void onSuccess(User user) {
                        Log.d(HttpConstant.TAG,user.toString());
                    }

                    @Override
                    protected void onError(String mess) {
                        Log.d(HttpConstant.TAG,mess.toString());
                    }
                });
    }

二、封装之旅

1、依赖
首先,要在build.gradle中添加相关的依赖,项目中用到Retrofit2,RxJava,Okhttp3,还用到Jackson、stetho、logger等。

compile 'com.squareup.okhttp3:okhttp:3.8.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:retrofit-converters:2.3.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
compile 'io.reactivex:rxjava:1.3.0'
compile 'io.reactivex:rxandroid:1.2.1'

2、定义Retrofit访问的接口

@FormUrlEncoded
@POST("login")
Observable<Result<User>> login(@FieldMap Map<String,String> params);

@GET("hot")
Observable<Result<String>> gethot(@QueryMap Map<String,String> params);

FormUrlEncoded指定表单形式提交,POST指定POST方式,括号是接口路径,与Retrofit.Builder的baseUrl拼接而成的。(注意:不能以 / 开头,因为baseUrl必须以 / 结尾)。其他注解方式请直接查看Retrofit2.0 官方文档

3、初始化Retrofit2
创建一个抽象类BaseRetrofit类,抽象类中含有一个抽象方法getBaseUrl和一个getOkHttpClient方法,其中getBaseUrl让子类实现获取url,getOkHttpClient获取OkHttpClient对象。

/**
 * author:hjy
 * date:2017/8/29 09:08
 * detail:Retrofit基类
 */

public abstract class BaseRetrofit {

    public Retrofit build()
    {
        Retrofit.Builder builder = new Retrofit.Builder();
        builder.baseUrl(getBaseUrl())
                .client(getOkHttpClient())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create());
        return enrichBuilder(builder).build();
    }

    protected abstract String getBaseUrl();
    // 默认值新建BaseOkhttpClient对象
    protected OkHttpClient getOkHttpClient(){
        return new BaseOkhttpClient().build();
    }

    protected Retrofit.Builder enrichBuilder(Retrofit.Builder builder) {
        return builder;
    }
}

4、初始化Okhttp3
创建BaseOkhttpClient基类,为了更好扩展,基类中公共方法richBuild让子类实现,进一步扩展OkHttpClient。扩展类有CacheOkhttpClient 带有数据缓存、HeaderOkhttpClient头部认证等,根据需求还可以扩展token、证书等。
BaseOkhttpClient.java

/**
 * author:hjy
 * date:2017/7/18 14:29
 * detail:okhttpClient基类
 */

public class BaseOkhttpClient {

    public OkHttpClient build()
    {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.addNetworkInterceptor(new StethoInterceptor())
                .retryOnConnectionFailure(true)
                .connectTimeout(HttpConstant.CONNECT_TIME, TimeUnit.SECONDS)
                .readTimeout(HttpConstant.READ_TIME, TimeUnit.SECONDS)
                .writeTimeout(HttpConstant.WRITE_TIME, TimeUnit.SECONDS)
                .build();
        return richBuild(builder).build();
    }

    protected OkHttpClient.Builder richBuild(OkHttpClient.Builder builder) {return builder;}
}

CacheOkhttpClient.java

/**
 * author:hjy
 * date:2017/7/18 15:05
 * detail:cacheOkhttpClient 带有数据缓存
 */

public class CacheOkhttpClient extends BaseOkhttpClient {

    private Context mContext;

    public CacheOkhttpClient(Context context) {
        this.mContext = context;
    }

    @Override
    protected OkHttpClient.Builder richBuild(OkHttpClient.Builder builder) {
        File cacheFile = new File(mContext.getCacheDir(), mContext.getString(R.string.app_name));
        Cache cache = new Cache(cacheFile, SIZE_OF_CACHE); //50Mb
        // 没有网络直接读取本地且本地缓存时间为最大值,有网络直接网络获取,
        // 如果想在有网络情况下,超过某个值进行请求,可以修改时间
        // 以这些只有针对get请求,其他请求无效
        builder.cache(cache).addNetworkInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                Response response = chain.proceed(request);
                if (NetUtil.hashNetWork(mContext)) {
                    return response.newBuilder()
                            .removeHeader("Pragma")
                            .header("Cache-Control", "public, max-age=" + MAX_CACHE_TIME)
                            .build();
                } else {
                    return response.newBuilder()
                            .removeHeader("Pragma")
                            .header("Cache-Control", CacheControl.FORCE_CACHE.toString())
                            .build();
                }
            }

        }).addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                if (!NetUtil.hashNetWork(mContext)) {
                    request = request.newBuilder()
                            .cacheControl(CacheControl.FORCE_CACHE)
                            .build();
                }
                return chain.proceed(request);
            }
        });

        return super.richBuild(builder);
    }
}

HeaderOkhttpClient.java

/**
 * author:hjy
 * date:2017/8/28 18:18
 * detail:头部认证
 */

public class HeaderOkhttpClient extends CacheOkhttpClient{

    private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public HeaderOkhttpClient(Context mContext)
    {
        super(mContext);
    }

    @Override
    protected OkHttpClient.Builder richBuild(OkHttpClient.Builder builder) {
        builder.addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                // key 键值对 决定需求的添加
                Request.Builder requestBuilder = request.newBuilder()
                        .addHeader("timestamp",dateFormat.format(new Date()))
                        .addHeader("encrypt", MD5Util.getMD5(dateFormat.format(new Date())+""));// 根据自己需求修改头部的key,这边只是一种时间戳和某个字符进行md5加密简单认证。
                Log.e(HttpConstant.TAG,"服务器返回数据:"+chain.proceed(requestBuilder.build()).body().string());
                return chain.proceed(requestBuilder.build());
            }
        });
        return super.richBuild(builder);
    }
}

5、服务器返回数据处理
假如服务器返回参数和字段如下:

{
   "errCode": 0,
   "res":"success!",
   "data":{
         "name":"张三",
          "age":3,
          "phone":"157xxxxxx"
   }
}

根据服务器返回报文一般都是统一格式的,为了方便把返回的参数进行分装成Result< T >,其中T泛型就是data对应的字段。详细代码如下:

/**
 * author:hjy
 * date:2017/8/29 10:33
 * detail:实体类基类
 */
public class Result implements Serializable {

    // 服务器回调码 0 成功 1 失败
    private int errCode;
    // 服务器提示语
    private String res;
    // json数据
    private T data;

    public int getErrCode() {
        return errCode;
    }

    public void setErrCode(int errCode) {
        this.errCode = errCode;
    }

    public String getRes() {
        return res;
    }

    public void setRes(String res) {
        this.res = res;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

定义完服务器数据格式后,定义RxDataHelper类对Observable整体的变换,换句话说对返回数据做转换处理。具体如下:

public class RxDataHelper {

    public static  Observable.Transformer,T> handResult()
    {
        return new Observable.Transformer, T>() {
            @Override
            public Observable call(Observable> resultObservable) {
                return resultObservable.flatMap(new Func1, Observable>() {
                    @Override
                    public Observable call(Result tResult) {
                        if (tResult == null)
                            return Observable.error(new HttpException(HttpErrorCode.CODE_DATA_ERROR, HttpErrorMsg.DATA_ERROR));
                        LogUtil.e(HttpConstant.TAG, "解析完服务器数据:" + tResult);
                        // 成功数据
                        if (tResult.getErrCode() == 0)
                            return Observable.just(tResult.getData());
                        // 数据失败
                        else if (tResult.getErrCode() == 1)
                            return Observable.error(new HttpException(tResult.getErrCode(),
                                    tResult.getRes()));
                        return Observable.empty();
                    }
                });
            }
        };
    }
}

假设tResult.getErrCode() == 0是代表服务器返回成功数据,tResult.getErrCode() == 1是代表服务器返回失败数据。服务器返回成功后,使用just(T…): 将传入的参数依次发送出来。服务器返回失败,对失败数据进一步处理。

服务器返回成功后,数据进一步处理。自定义RxSubscriber继承Subscriber< T >订阅,重写onCompleted,onError,onNext这三方法。

public abstract class RxSubscriber extends Subscriber {

    private Context mContext;


    public RxSubscriber(Context context) {
        this.mContext = context;
    }

    @Override
    public void onCompleted() {
        LogUtil.e(HttpConstant.TAG, "=========================服务器请求结束(onCompleted)==============================");
    }

    @Override
    public void onError(Throwable e) {
        e.printStackTrace();
        Log.e(HttpConstant.TAG,"e:" + e);
        String exceptionStr = HttpErrorMsg.UKNOW;
        int errorCode = HttpErrorCode.CODE_UNKNOW;
        // 自定义异常
        if (e instanceof HttpException) {
            HttpException eStr = (HttpException) e;
            errorCode = eStr.getmErrorCode();
            exceptionStr = eStr.getmMessage();
        }
        // 解析异常
        else if (e instanceof JSONException
                || e instanceof ParseException) {
            exceptionStr = HttpErrorMsg.DATA_ERROR;
            errorCode = HttpErrorCode.CODE_DATA_ERROR;
        }
        // 连接超时、失败
        else if (e instanceof ConnectException ||
                e instanceof SocketTimeoutException){
            exceptionStr = HttpErrorMsg.TIME_OUT;
            errorCode = HttpErrorCode.CODE_TIME_OUT;
        }
        Toast.makeText(mContext,exceptionStr,Toast.LENGTH_SHORT).show();
        LogUtil.e(HttpConstant.TAG,"errorCode:"+errorCode+" mess:"+exceptionStr);
        LogUtil.e(HttpConstant.TAG,"=========================服务器请求结束(onError)==============================");
        onError(exceptionStr);
    }

    @Override
    public void onNext(T t) {
        onSuccess(t);
    }

    @Override
    public void onStart() {
        LogUtil.e(HttpConstant.TAG,"========================服务器请求开始(onStart)===============================");
        if (!NetUtil.hashNetWork(mContext)) {
            Toast.makeText(mContext,"请检查您的网络!",Toast.LENGTH_SHORT).show();
            LogUtil.e(HttpConstant.TAG,"请检查您的网络!");
            return;
        }
    }

    protected abstract void onSuccess(T t);

    protected abstract void onError(String mess);

}

对onError异常进一步处理,自定义HttpException对象,包含mErrorCode错误码、mMessage提示异常提示、mCause异常。
HttpException.java

public class HttpException extends Exception {

    private int mErrorCode;
    private String mMessage;
    private Throwable mCause;

    public HttpException(int errorCode, String message) {
        super(message);
        this.mMessage = message;
        this.mErrorCode = errorCode;
    }

    public HttpException(int errorCode, String message, Throwable cause) {
        super(message, cause);
        this.mMessage = message;
        this.mErrorCode = errorCode;
        this.mCause = cause;
    }

    public int getmErrorCode() {
        return mErrorCode;
    }

    public void setmErrorCode(int mErrorCode) {
        this.mErrorCode = mErrorCode;
    }

    public String getmMessage() {
        return mMessage;
    }

    public void setmMessage(String mMessage) {
        this.mMessage = mMessage;
    }

    public Throwable getmCause() {
        return mCause;
    }

    public void setmCause(Throwable mCause) {
        this.mCause = mCause;
    }
}

6、线程控制Scheduler
Schedulers.immediate(): 直接在当前线程运行,相当于不指定线程。这是默认的 Scheduler。
Schedulers.newThread(): 总是启用新线程,并在新线程执行操作。
Schedulers.io(): I/O 操作(读写文件、读写数据库、网络信息交互等)所使用的 Scheduler。行为模式和 newThread() 差不多,区别在于 io() 的内部实现是是用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下 io() 比 newThread() 更有效率。不要把计算工作放在 io() 中,可以避免创建不必要的线程。
Schedulers.computation(): 计算所使用的 Scheduler。这个计算指的是 CPU 密集型计算,即不会被 I/O 等操作限制性能的操作,例如图形的计算。这个 Scheduler 使用的固定的线程池,大小为 CPU 核数。不要把 I/O 操作放在 computation() 中,否则 I/O 操作的等待时间会浪费 CPU。
另外, Android 还有一个专用的 AndroidSchedulers.mainThread(),它指定的操作将在 Android 主线程运行。

创建一个线程调度工具类:

public class RxSchedulersHelper {

    public static  Observable.Transformer doIoAndMain() {
        return new Observable.Transformer() {
            @Override
            public Observable call(Observable tObservable) {
                return tObservable.subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }
        };
    }
}

具体代码查看:
GitHub项目地址:https://github.com/hjy15759633702/TestHttp

你可能感兴趣的:(Android实战)