Okhttp3+Rxjava+Retrofit2封装

来源:互联网 发布:郭敬明作品集软件下载 编辑:程序博客网 时间:2024/05/19 16:22

一、简介

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<String,String> params = new HashMap<>();        params.put("account","test");        params.put("password","test");        Observable<Result<User>> observable = HttpManager.init().with(MainActivity.this).login(params);        observable.compose(RxSchedulersHelper.<Result<User>>doIoAndMain())                .compose(RxDataHelper.<User>handResult(LifeCycleEvent.DESTROY,lifecycleSubject))                .subscribe(new RxSubscriber<User>(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<T> 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 <T> Observable.Transformer<Result<T>,T> handResult()    {        return new Observable.Transformer<Result<T>, T>() {            @Override            public Observable<T> call(Observable<Result<T>> resultObservable) {                return resultObservable.flatMap(new Func1<Result<T>, Observable<T>>() {                    @Override                    public Observable<T> call(Result<T> 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<T> extends Subscriber<T> {    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 <T> Observable.Transformer<T,T> doIoAndMain() {        return new Observable.Transformer<T,T>() {            @Override            public Observable<T> call(Observable<T> tObservable) {                return tObservable.subscribeOn(Schedulers.io())                        .observeOn(AndroidSchedulers.mainThread());            }        };    }}

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

原创粉丝点击