android:Retrofit+RxJava的优雅封装

image.png

前言

RetrofitRxJava已经出来很久了,很多前辈写了很多不错的文章,在此不得不感谢这些前辈无私奉献的开源精神,能让我们站在巨人的肩膀上望得更远。

Retrofit:Retrofit是Square 公司开发的一款针对Android 网络请求的框架。
RxJava:RxJava 是一个链式调用的异步框架。

RxJava在 GitHub 主页上的自我介绍是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库)。这就是 RxJava ,概括得非常精准。

一言以辟之,就是让异步操作变得非常简单。
各自职责:Retrofit 负责请求的数据和请求的结果,使用接口的方式呈现,OkHttp 负责请求的过程,RxJava 负责异步,各种线程之间的切换。

RxJava的使用参考-->给 Android 开发者的 RxJava 详解
Retrofit的使用参考-->Android Retrofit 2.0使用

本文内容是基于Retrofit + RxJava做的一些优雅的封装。参考了很多文章加入了一些自己的理解,请多指教。


先引入依赖

build.gradle

    // Okhttp库
    implementation 'com.squareup.okhttp3:okhttp:3.11.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.8.1'
    // Retrofit库
    implementation 'com.squareup.retrofit2:retrofit:2.1.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
    //RxJava
    implementation 'io.reactivex.rxjava2:rxjava:2.0.1'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'

在此之前,

先来聊聊简单使用,(优雅封装见下文)

1、先写一个接口Service
interface APIService {
    @GET("user/login" )
    Call<UserInfo> login(@Query("username") String username,@Query("password")String password);
}
2、获取Call执行网络请求
Retrofit retrofit = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(BASE_URL)
                .build();
        APIService service = retrofit.create(APIService.class);

        Call<UserInfo> call = service.login("张晓宇", "is sb");
        call.enqueue(new Callback<UserInfo>() {
            @Override
            public void onResponse(Call<UserInfo> call, Response<UserInfo> response) {
                //请求成功
            }
            @Override
            public void onFailure(Call<UserInfo> call, Throwable t) {
                //请求失败
            }
        });

以上就是Retrofit的简单使用。但是实际项目中,我们肯定不会这么写,这样写完全不符合我们写代码的优雅性和简洁性。所以,我们要对它进行优雅的封装。

Retrofit+RxJava优雅的封装

1、请求实体类与返回实体类的封装

BaseRequestEntity.java

public class BaseRequestEntity <T>{
    private HeaderEntity header;
    private T data;

    public HeaderEntity getHeader() {
        return header;
    }

    public void setHeader(HeaderEntity header) {
        this.header = header;
    }

    public T getData() {
        return data;
    }

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

BaseResponseEntity.java

public class BaseResponseEntity<T> {
    private int errorCode;
    private String errorMsg;
    private T data;

    public int getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(int errorCode) {
        this.errorCode = errorCode;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }

    public T getData() {
        return data;
    }

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

2、请求接口服务类封装

ObservableAPI.java

/**
 * 接口服务
 **/
public interface ObservableAPI {

    /**
     * 登录
     */
    @POST(URL.URL_LOGIN)
    Observable<BaseResponseEntity<LoginResponseEntity>> login(@Body BaseRequestEntity<LoginRequestEntity> requestEntity);

}

3、ObservableManager封装(请求接口传参封装)

ObservableManager.java

public class ObservableManager {

    private static class SingletonHolder {
        static final ObservableManager INSTANCE = new ObservableManager();
    }

    public static ObservableManager getInstance() {
        return ObservableManager.SingletonHolder.INSTANCE;
    }

    /**
     * login
     */
    public BaseRequestEntity<LoginRequestEntityl> getLoginRequestEntity() {
        BaseRequestEntity<LoginRequestEntity> requestModel = new BaseRequestEntity<>();
        requestModel.setHeader(HeaderUtils.setHeaderModel());
        LoginRequestEntity loginRequestModel = new LoginRequestEntity();
        requestModel.setData(loginRequestModel);
        return requestModel;
    }

}

4、Retrofit封装

RetrofitHandler.java

public class RetrofitHandler {
    private static Retrofit mRetrofit;
    private static OkHttpClient mOkHttpClient;
    private static RetrofitHandler mRetrofitHandler;
    private static ObservableAPI mObservableAPI;

    private RetrofitHandler() {
        initRetrofit();
    }

    public static synchronized RetrofitHandler getInstance() {
        if (mRetrofitHandler == null) {
            synchronized (RetrofitHandler.class) {
                if (mRetrofitHandler == null) {
                    mRetrofitHandler = new RetrofitHandler();
                }
            }
        }
        return mRetrofitHandler;
    }

    /**
     * 获取 Retrofit
     */
    private void initRetrofit() {
        initOkHttpClient();
        mRetrofit = new Retrofit.Builder()
                .baseUrl(URL.BASE_URL)
                //JSON转换器,使用Gson来转换
                .addConverterFactory(GsonConverterFactory.create())
                //RxJava适配器
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(mOkHttpClient)
                .build();
        mObservableAPI = mRetrofit.create(ObservableAPI.class);
    }

    /**
     * 单例模式获取 OkHttpClient
     */
    private static void initOkHttpClient() {
        if (mOkHttpClient == null) {
            synchronized (RetrofitHandler.class) {
                if (mOkHttpClient == null) {
                    // 指定缓存路径,缓存大小100Mb
                    Cache cache = new Cache(new File(HttpConfig.DIR_CACHE_FILE, "HttpCache"),
                            1024 * 1024 * 100);
                    mOkHttpClient = new OkHttpClient.Builder()
                            //设置连接超时时间
                            .connectTimeout(HttpConfig.HTTP_TIME_OUT_TIME, TimeUnit.SECONDS)
                            //设置读取超时时间
                            .readTimeout(HttpConfig.HTTP_TIME_OUT_TIME, TimeUnit.SECONDS)
                            //设置写入超时时间
                            .writeTimeout(HttpConfig.HTTP_TIME_OUT_TIME, TimeUnit.SECONDS)
                            //默认重试一次
                            .retryOnConnectionFailure(true)
                            //添加请求头拦截器
                            .addInterceptor(InterceptorHelper.getHeaderInterceptor())
                            //添加日志拦截器
                            .addInterceptor(InterceptorHelper.getLogInterceptor())
                            //添加缓存拦截器
                            .addInterceptor(InterceptorHelper.getCacheInterceptor())
                            //添加重试拦截器
                            .addInterceptor(InterceptorHelper.getRetryInterceptor())
                            // 信任Https,忽略Https证书验证
                            // https认证,如果要使用https且为自定义证书 可以去掉这两行注释,并自行配制证书。
                            .sslSocketFactory(SSLSocketTrust.getSSLSocketFactory())
                            .hostnameVerifier(SSLSocketTrust.getHostnameVerifier())
                            //缓存
                            .cache(cache)
                            .build();
                }
            }
        }
    }

    /**
     * 对外提供调用 API的接口
     *
     * @return
     */
    public ObservableAPI getAPIService() {
        return mObservableAPI;
    }
}

5、拦截器封装(请求头拦截器、日志拦截器、缓存拦截器、重试拦截器等)

InterceptorHelper.java

/**
 * @author wy
 * @description 拦截器工具类
 */

public class InterceptorHelper {
    public static String TAG = "Interceptor";

    /**
     * 日志拦截器
     */
    public static HttpLoggingInterceptor getLogInterceptor() {
        return new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                Log.w(TAG, "LogInterceptor---------: " + message);
            }
        }).setLevel(HttpLoggingInterceptor.Level.BODY);//设置打印数据的级别
    }

    /**
     * 缓存拦截器
     *
     * @return
     */
    public static Interceptor getCacheInterceptor() {
        return new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                //CONTEXT不能为空
                if (!NetworkUtils.isConnected(PalApplication.getInstance().getApplicationContext())) {
                    int maxStale = 4 * 7 * 24 * 60; // 离线时缓存保存4周,单位:秒
                    CacheControl tempCacheControl = new CacheControl.Builder()
                            .onlyIfCached()
                            .maxStale(maxStale, TimeUnit.SECONDS)
                            .build();
                    request = request.newBuilder()
                            .cacheControl(tempCacheControl)
                            .build();
                }
                return chain.proceed(request);
            }
        };
    }


    /**
     * 重试拦截器
     *
     * @return
     */
    public static Interceptor getRetryInterceptor() {
        return new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                int maxRetry = 10;//最大重试次数
                int retryNum = 5;//假如设置为3次重试的话,则最大可能请求4次(默认1次+3次重试)

                Request request = chain.request();
                Response response = chain.proceed(request);
                while (!response.isSuccessful() && retryNum < maxRetry) {
                    retryNum++;
                    response = chain.proceed(request);
                }
                return response;
            }
        };
    }

    /**
     * 请求头拦截器
     *
     * @return
     */
    public static Interceptor getHeaderInterceptor() {
        return new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                //在这里你可以做一些想做的事,比如token失效时,重新获取token
                //或者添加header等等


                Request originalRequest = chain.request();

                if (null == originalRequest.body()) {
                    return chain.proceed(originalRequest);
                }

                Request compressedRequest = originalRequest.newBuilder()
                        .header("Content-Encoding", "gzip")
                        .header("User-Agent", "OkHttp Headers.java")
                        .addHeader("Accept", "application/json; q=0.5")
                        .addHeader("Accept", "application/vnd.github.v3+json")
                        .addHeader("Accept-Encoding", "identity")
//                    .addHeader(Constants.WEB_TOKEN, webi_token)
                        .build();
                Response proceed = chain.proceed(compressedRequest);
                return proceed;
            }
        };

    }
}

6、BaseObserver封装(请求失败、网络异常、接口错误、加载窗口等处理)

BaseObserver.java


public abstract class BaseObserver<T> implements Observer<BaseResponseEntity<T>> {
    protected Context mContext;

    public BaseObserver() {

    }

    public BaseObserver(Context cxt) {
        this.mContext = cxt;
    }

    @Override
    public void onSubscribe(Disposable d) {
        onRequestStart();
    }

    @Override
    public void onNext(BaseResponseEntity<T> tBaseEntity) {
        onRequestEnd();
        String message_common = "Oops, something went wrong. Please try again.";
        if (tBaseEntity.getErrorCode()==0) {//成功
            try {
                onSuccess(tBaseEntity);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            try {
                if (!CommonUtils.isEmptyOrNull(tBaseEntity.getErrorMsg())) {
                    onFailure(tBaseEntity.getErrorMsg());
                } else {
                    onFailure(message_common);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onError(Throwable e) {
        onRequestEnd();
        String message_common = "Oops, something went wrong. Please try again.";
        String msg_timeout = "Oops, connection timeout, please try again later";
        try {
            if (e instanceof ConnectException
                    || e instanceof TimeoutException
                    || e instanceof NetworkErrorException
                    || e instanceof UnknownHostException) {
                onFailure(msg_timeout);
            } else {
                onFailure(message_common);
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

    @Override
    public void onComplete() {

    }

    /**
     * 返回成功
     *
     * @param tBaseEntity
     */
    protected abstract void onSuccess(BaseResponseEntity<T> tBaseEntity);

    /**
     * 返回失败
     *
     * @param errorMessage
     */
    protected abstract void onFailure(String errorMessage);

    /**
     * 请求开始
     */
    protected void onRequestStart() {
        showProgressDialog();
    }


    /**
     * 请求结束
     */
    protected void onRequestEnd() {
        closeProgressDialog();
    }

    /**
     * 加载弹窗
     */
    public void showProgressDialog() {

    }

    /**
     * 关闭加载弹窗
     */
    public void closeProgressDialog() {

    }

}

7、调度类封装

RxTransformerHelper.java

/**
 * 调度类
 */
public class RxTransformerHelper {

    public static <T> ObservableTransformer<T, T> observableIO2Main(final Context context) {
        return new ObservableTransformer<T, T>() {
            @Override
            public ObservableSource<T> apply(Observable<T> upstream) {
                return upstream.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
            }
        };
    }
}

8、使用方式

private void login() {
        RetrofitHandler.getInstance().getAPIService()
                .login(ObservableManager.getInstance().getLoginRequestEntity())
                .compose(RxTransformerHelper.<BaseResponseEntity<LoginResponseEntity>>observableIO2Main(this))
                .subscribe(new BaseObserver<LoginResponseEntity>() {
                    @Override
                    protected void onSuccess(BaseResponseEntity<LoginResponseEntity> responseEntity) {
                        showSuccessDialog("Success");
                    }

                    @Override
                    protected void onFailure(String errorMessage) {
                        showErrorDialog(errorMessage);
                    }

                });
    }

9、到这里,Retrofit+RxJava基本算是优雅的封装完成了,其实,如果追求更完美的话,还可以进行二次封装,将第八步的请求封装的更为简洁。这个,就看各人喜好而定了,本文不再赘述。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,568评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,473评论 2 59
  • 这是信息泛滥,关系为王的年代,一个人赚的钱,12.5%来自于自己的知识,87.5%来源于所谓的人脉。 话说,十年前...
    笑忘书800阅读 368评论 2 2
  • 个人的资质,反映了经验和本领,但谦逊的品质,却反映了层次和境界。例如:一间教室里聚满等待着观摩一个传说中的精英的面...
    吕明超阅读 142评论 0 0
  • 转眼之间,2017春节将要过去了,过年因为种种原因没有回家,大年三十开始休息了三天,就投入了热火朝天的工作之中,因...
    容若的小弟子阅读 371评论 0 0