rxjava、retrofit 简单&完善封装

该封装中除了原有的功能,额外包含什么功能先说一下,另外先展示基本使用方式。看是不是你想要的

1、公共参数的添加

2、token的自动刷新

3、错误请求的自动处理

4、单例模式以及工程模式的retrofit,以适应出现不同域名请求的情况

使用方式如下

1、接口方法定义

    /**
     * 示例
     * 上传名字
     * <BaseResponse<Object>> BaseResponse为封装的公共请求结果实体,Object可以改成对应自己想要的实体
     */
    @POST("*********")
    Observable<BaseResponse<Object>> uploadName(@Query("name") String name);

2、调用方法

        RxHelper.deploy(RetrofitUtil.getRetrofit().uploadName("小明"), new NetCallback<StudentInfo>() {
            @Override
            public void onCompleted(StudentInfo studentInfo) {
            //StudentInfo studentInfo 这个要和 方法定义中的BaseResponse的泛型<studentInfo>保持一致
            }
        });

       以上就是使用的方法,写这个的初衷是因为我懒,抱着磨刀不误砍柴工的想法,所以封装的尽可能简单。之后我会对该封装做非常细致的说明,每个类名称,和其中所负责的功能都会以文字描述,之后配上该类的整体代码。尽可能做到复制之后可以直接使用,也希望让你看了之后有所借鉴。(需要看的文字简述不多,代码复制可直接使用)

一、首先需要导入的库如下

        implementation 'com.squareup.retrofit2:retrofit:2.1.0'
        implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
        implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
        implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0'
        implementation 'io.reactivex:rxjava:1.0.14'
        implementation 'io.reactivex:rxandroid:1.0.1'

二、类名“RetrofitUtil” , retrofit初始化,生产实例的工具类,一般初始化的过程类似,配置参数不同,代码中对配置的参数做详细说明

public class RetrofitUtil {
    private static ApiManage api;
    /**
     * 一般调用方式,用于服务器域名相同的情况
     */
    public static ApiManage getRetrofit() {
        if(api==null){
            Gson gson=new GsonBuilder()
                    .serializeNulls()
                    .setLenient()
                    .create();
            api = new Retrofit.Builder()
                    .baseUrl(Api.BASE_URL) //服务器域名 Api,就是存放通用域名的接口,也可以是静态类,自己写一个,或者直接把地址些里面也可
                    .client(OkHttpClientHelper.getInstance().getOkHttpClient()) //请求预处理
                    .addConverterFactory(GsonConverterFactory.create(gson)) //gson自动解析配置
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) //使用Rxjava自定义adapter,通常用法
                    .build().create(ApiManage.class); //发起请求的接口
        }
        return api;
    }

    /**
     * 用于服务器域名不同的情况,传入服务器域名
     */
    public static ApiManage createRetrofitFrom(String baseUrl) {
        if (baseUrl == null) {
            baseUrl = Api.BASE_URL;
        }
        Gson gson=new GsonBuilder()
                .serializeNulls()
                .setLenient()
                .create();
        ApiManage api = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .client(OkHttpClientHelper.getInstance().getOkHttpClient())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build().create(ApiManage.class);
        return api;
    }
}

三、类名“OkHttpClientHelper” ,请求预处理,主要增加了公共参数的添加,以及请求链接的打印。这里代码做了判断分别进行参数添加,是因为请求以请求体方式和拼接方式请求时,添加参数方式有所不同。使用时记得将公共参数名和值改成自己需要的。

public class OkHttpClientHelper {
    private OkHttpClient mOkHttpClient;
    private static final int TIME_OUT_LIMIT = 60 * 1000;//超时时间

    /**
     * 获取OkHttpClient
     * @return
     */
    public OkHttpClient getOkHttpClient() {
        if (mOkHttpClient == null) {
            OkHttpClient.Builder builder = new OkHttpClient.Builder()
                    //设置超时时间
                    .connectTimeout(TIME_OUT_LIMIT, TimeUnit.MILLISECONDS)
                    .readTimeout(TIME_OUT_LIMIT, TimeUnit.MILLISECONDS)
                    .writeTimeout(TIME_OUT_LIMIT, TimeUnit.MILLISECONDS)
                    .retryOnConnectionFailure(true);//错误重连开启

            //添加公共参数
            builder.addInterceptor(new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request request = chain.request();
                    if ("POST".equals(request.method())||"GET".equals(request.method())) {
                        if (request.body() instanceof FormBody) {
                            FormBody.Builder bodyBuilder = new FormBody.Builder();
                            FormBody formBody = (FormBody) request.body();
                            // 先复制原来的参数
                            for (int i = 0; i < formBody.size(); i++) {
                                bodyBuilder.addEncoded(formBody.encodedName(i), formBody.encodedValue(i));
                            }
                            formBody = bodyBuilder
                                    .addEncoded("公共参数名", "公共参数值")
                                    .addEncoded("公共参数名", "公共参数值")
                                    .build();
                            request = request.newBuilder().post(formBody).build();
                        }else {
                            HttpUrl httpUrl = request.url()
                                    .newBuilder()
                                    .addQueryParameter("公共参数名","公共参数值")
                                    .build();
                            request = request.newBuilder().url(httpUrl).build();
                        }
                    }
                    return chain.proceed(request);
                }
            });

            //添加拦截打印请求日志
            HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
                @Override
                public void log(String message) {
                    Log.i("http-request", message);
                }
            });
            interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
            builder.addInterceptor(interceptor);
            mOkHttpClient = builder.build();
        }
        return mOkHttpClient;
    }

    private OkHttpClientHelper() {
    }

    public static OkHttpClientHelper getInstance() {
        return LazyHolder.INSTANCE;
    }

    private static final class LazyHolder {
        private static final OkHttpClientHelper INSTANCE = new OkHttpClientHelper();
    }
}

四、类名“RxHelper” , rxjava的帮助类,针对返回值做了统一处理,也对使用rxjava请求做了一些简化处理。token的刷新被注释掉了,如果需要token的自动刷新并从新请求原接口,注释的部分就很有必要看一下,里面注释很清楚。返回值的code判断请根据自己服务器返回的内容进行修改

public class RxHelper {
    private static int refreshTokenNumber = 0;

    private static Observable threadControl(Observable observable) {
        return observable //线程转换的常用方式,封装节约调用步骤
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io());
    }

    private static Subscription subscribe(final Observable observable, final NetCallback callback) {
        return observable
                .subscribe(new Subscriber<BaseResponse>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void one rror(Throwable e) {
                        callback.onFailed("系统错误,请联系管理员");
                        ErrorRequestProcess.throwableErrorRequest(e);
                    }

                    @Override
                    public void onNext(BaseResponse baseResponse) {
                        if (null != baseResponse) {
                            if (baseResponse.getCode() == 503) { //判断token过期,重新获取token,并发起原请求
                                refreshToken(observable, callback); //自定义刷新token方法
                                return;
                            }
                            if (baseResponse.getCode() == 200) { //请求成功返回请求结果
                                callback.onCompleted(baseResponse.getData());
                            } else {
                                callback.onFailed(baseResponse.getMsg());
                                ErrorRequestProcess.codeErrorRequest(baseResponse.getCode());
                            }
                        } else {
                            callback.onFailed("返回格式错误");
                            ErrorRequestProcess.codeErrorRequest(0);
                        }
                    }
                });
    }

    /**
     * token刷新,该方法根据需要自行配置,如果请求中不需要刷新直接打印token过期就好
     * @param observable
     * @param callback
     */
    private static void refreshToken(final Observable observable, final NetCallback callback) {
        ToastUtil.showshortToastInButtom(MyApplication.getContext(),"token过期");
//        if (refreshTokenNumber > 100) {//针对token的非过期错误的极端措施/因为过期没有单独的返回值
//            Toast.makeText(MyApplication.getContext(), "token出现未知错误", Toast.LENGTH_LONG).show();
//            refreshTokenNumber = 80;
//            return;
//        }
//        refreshTokenNumber++;
//
//        Log.e("<请求错误log>", "CodeError=====: token验证失效,重新获取");
//        //刷新token请求的参数预处理,这里根据你的刷新token接口替换
//        String dateTime = String.valueOf(System.currentTimeMillis());
//        StringBuilder stringBuilder = new StringBuilder();
//        stringBuilder.append(Constants.appKey).append(dateTime).append(Constants.appSecret);
//        String sign = MD5.md5(stringBuilder.toString());
//        //刷新token请求,换成你直接的请求,请求写在ApiManage中
//        deploy(RetrofitUtil.getRetrofit().auth(Constants.appKey, dateTime, sign), new NetCallback<String>() {
//            @Override
//            public void onCompleted(String s) {
//                new SharedPreferencesHelper().put("token", s); //这里存储token并全局化,一般是在公共参数出添加
//                deploy(observable, callback); //这里固定的调用旧的接口就好,记得本地token的更新处理
//            }
//        });
    }

    /**
     * 接口请求并返回结果
     * @param observable 接口方法
     * @param callback 请求结果的接口回调
     * @return
     */
    public static Subscription deploy(Observable observable, final NetCallback callback) {
        Subscription mSubscription = subscribe(threadControl(observable), callback);
        return mSubscription;
    }
}

五、接口名“ApiManage” ,请求接口,返回类型增加rxjava后改为Observable,@Query注解可以用于一般情况,请求参数直接拼接到请求地址上。

public interface ApiManage {
    /**
     * 这只是一个实例
     */
    @POST("这里是你自己请求的地址") //post是请求方式
    Observable<BaseResponse<你自己的返回的实体类>> uploadName(@Query("你自己的参数名") String 参数名,@Query("你自己的参数名") String 参数名);

}

六、类名“NetCallback” ,统一的请求回调,这里用了抽象类作为回调接口,方便不进行实现失败的回调,因为失败的回调已经做了统一处理。使用时,在结果回调失败时如果有特殊逻辑,可以直接实现进行。

public abstract class NetCallback<T> {
    /**
     * 完成
     * @param t
     */
    public abstract void onCompleted(T t);

    /**
     * 失败
     * @param errMsg
     */
    public void onFailed(String errMsg) {

    }
}

七、类名“BaseResponse”,返回结果的基础映射类,需要和你服务器返回的通常参数做对应,data的实例根据参数单独创建。该类的使用方式可参考顶部的接口方法定义。

public class BaseResponse<T> {
    private int code;
    private String msg;
    private T data;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

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

八、类名“ErrorRequestProcess”,错误的统一处理类,根据服务器返回code进行打印的地方自根据服务器错误对应的内容改一下

public class ErrorRequestProcess {

    public static void throwableErrorRequest(Throwable e) {
        String errorMsg = "未知错误";
        if (e instanceof UnknownHostException) {
            errorMsg = "网络不可用";
        } else if (e instanceof SocketTimeoutException) {
            errorMsg = "请求网络超时";
        } else if (e instanceof HttpException) {
            HttpException httpException = (HttpException) e;
            errorMsg = convertStatusCode(httpException);
        } else if (e instanceof ParseException || e instanceof JSONException
                || e instanceof JSONException) {
            errorMsg = "数据解析错误";
        }
        logThrowableError(errorMsg);
    }

    private static String convertStatusCode(HttpException httpException) {
        String msg;
        if (httpException.code() >= 500 && httpException.code() < 600) {
            msg = "服务器处理请求出错";
        } else if (httpException.code() >= 400 && httpException.code() < 500) {
            msg = "服务器无法处理请求";
        } else if (httpException.code() >= 300 && httpException.code() < 400) {
            msg = "请求被重定向到其他页面";
        } else {
            msg = "↑↑↑错误信息↑↑↑";
            logThrowableError("未知错误",httpException.message());
        }
        return msg;
    }

    /**
     * 这里是服务器返回的code,根据服务器的规定自行打印错误日志
     * @param errorCode
     */
    public static void codeErrorRequest(int errorCode) {
        switch (errorCode) {
            case -1:
                logCodeError("未知错误");
                return;
            case 400:
                logCodeError("参数错误");
                return;
        }
        logCodeError("未知错误"+errorCode);
    }

    public static void logCodeError(String error) {
        Log.e("<请求错误log>", "CodeError=====: " + error);
        Toast.makeText(MyApplication.getContext(),error,Toast.LENGTH_SHORT).show();
    }

    public static void logThrowableError(String error) {
        Log.e("<请求错误log>", "ThrowableError=====: " + error);
        Toast.makeText(MyApplication.getContext(),error,Toast.LENGTH_SHORT).show();
    }

    public static void logThrowableError(String error,String errorLog) {
        Log.e("<请求错误log>", "ThrowableError=====: " + errorLog);
        Toast.makeText(MyApplication.getContext(),error,Toast.LENGTH_SHORT).show();
    }
}

到此就结束了,这是第二次心血来潮写了篇博客。nnd还挺累,我看见别人有贴上自己的收款码的,不知道我......

 

那我就不客气了,抱拳感谢

rxjava、retrofit 简单&完善封装rxjava、retrofit 简单&完善封装

 

上一篇:RxJava源码阅读理解系列(四)


下一篇:设计模式Copy-on-write