android:Retrofit+RxJava的優(yōu)雅封裝

image.png

前言

RetrofitRxJava已經(jīng)出來很久了油讯,很多前輩寫了很多不錯的文章,在此不得不感謝這些前輩無私奉獻的開源精神延欠,能讓我們站在巨人的肩膀上望得更遠陌兑。

Retrofit:Retrofit是Square 公司開發(fā)的一款針對Android 網(wǎng)絡(luò)請求的框架。
RxJava:RxJava 是一個鏈式調(diào)用的異步框架由捎。

RxJava在 GitHub 主頁上的自我介紹是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一個在 Java VM 上使用可觀測的序列來組成異步的兔综、基于事件的程序的庫)。這就是 RxJava 狞玛,概括得非常精準软驰。

一言以辟之,就是讓異步操作變得非常簡單心肪。
各自職責:Retrofit 負責請求的數(shù)據(jù)和請求的結(jié)果锭亏,使用接口的方式呈現(xiàn),OkHttp 負責請求的過程硬鞍,RxJava 負責異步慧瘤,各種線程之間的切換。

RxJava的使用參考-->給 Android 開發(fā)者的 RxJava 詳解
Retrofit的使用參考-->Android Retrofit 2.0使用

本文內(nèi)容是基于Retrofit + RxJava做的一些優(yōu)雅的封裝固该。參考了很多文章加入了一些自己的理解锅减,請多指教。


先引入依賴

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'

在此之前伐坏,

先來聊聊簡單使用怔匣,(優(yōu)雅封裝見下文)

1、先寫一個接口Service
interface APIService {
    @GET("user/login" )
    Call<UserInfo> login(@Query("username") String username,@Query("password")String password);
}
2桦沉、獲取Call執(zhí)行網(wǎng)絡(luò)請求
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的簡單使用每瞒。但是實際項目中,我們肯定不會這么寫纯露,這樣寫完全不符合我們寫代碼的優(yōu)雅性和簡潔性独泞。所以,我們要對它進行優(yōu)雅的封裝苔埋。

Retrofit+RxJava優(yōu)雅的封裝

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、請求接口服務(wù)類封裝

ObservableAPI.java

/**
 * 接口服務(wù)
 **/
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轉(zhuǎn)換器,使用Gson來轉(zhuǎn)換
                .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()
                            //設(shè)置連接超時時間
                            .connectTimeout(HttpConfig.HTTP_TIME_OUT_TIME, TimeUnit.SECONDS)
                            //設(shè)置讀取超時時間
                            .readTimeout(HttpConfig.HTTP_TIME_OUT_TIME, TimeUnit.SECONDS)
                            //設(shè)置寫入超時時間
                            .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();
                }
            }
        }
    }

    /**
     * 對外提供調(diào)用 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);//設(shè)置打印數(shù)據(jù)的級別
    }

    /**
     * 緩存攔截器
     *
     * @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;//最大重試次數(shù)
                int retryNum = 5;//假如設(shè)置為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封裝(請求失敗腹暖、網(wǎng)絡(luò)異常汇在、接口錯誤、加載窗口等處理)

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();
    }


    /**
     * 請求結(jié)束
     */
    protected void onRequestEnd() {
        closeProgressDialog();
    }

    /**
     * 加載彈窗
     */
    public void showProgressDialog() {

    }

    /**
     * 關(guān)閉加載彈窗
     */
    public void closeProgressDialog() {

    }

}

7脏答、調(diào)度類封裝

RxTransformerHelper.java

/**
 * 調(diào)度類
 */
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基本算是優(yōu)雅的封裝完成了阿蝶,其實,如果追求更完美的話黄绩,還可以進行二次封裝羡洁,將第八步的請求封裝的更為簡潔。這個爽丹,就看各人喜好而定了筑煮,本文不再贅述。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末习劫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子嚼隘,更是在濱河造成了極大的恐慌诽里,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件飞蛹,死亡現(xiàn)場離奇詭異谤狡,居然都是意外死亡,警方通過查閱死者的電腦和手機卧檐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門墓懂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人霉囚,你說我怎么就攤上這事捕仔。” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵榜跌,是天一觀的道長闪唆。 經(jīng)常有香客問我,道長钓葫,這世上最難降的妖魔是什么悄蕾? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮础浮,結(jié)果婚禮上帆调,老公的妹妹穿的比我還像新娘。我一直安慰自己豆同,他們只是感情好番刊,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪圆裕。 梳的紋絲不亂的頭發(fā)上胳蛮,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音躯砰,去河邊找鬼。 笑死,一個胖子當著我的面吹牛沃但,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播佛吓,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼宵晚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了维雇?” 一聲冷哼從身側(cè)響起淤刃,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吱型,沒想到半個月后逸贾,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡津滞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年铝侵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片触徐。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡咪鲜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出撞鹉,到底是詐尸還是另有隱情疟丙,我是刑警寧澤颖侄,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站隆敢,受9級特大地震影響发皿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拂蝎,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一穴墅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧温自,春花似錦玄货、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至馆里,卻和暖如春隘世,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鸠踪。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工丙者, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人营密。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓械媒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親评汰。 傳聞我的和親對象是個殘疾皇子纷捞,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,504評論 25 707
  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料被去? 從這篇文章中你...
    hw1212閱讀 12,693評論 2 59
  • 這是信息泛濫惨缆,關(guān)系為王的年代糜值,一個人賺的錢,12.5%來自于自己的知識踪央,87.5%來源于所謂的人脈臀玄。 話說瓢阴,十年前...
    笑忘書800閱讀 393評論 2 2
  • 個人的資質(zhì)畅蹂,反映了經(jīng)驗和本領(lǐng),但謙遜的品質(zhì)荣恐,卻反映了層次和境界液斜。例如:一間教室里聚滿等待著觀摩一個傳說中的精英的面...
    呂明超閱讀 158評論 0 0
  • 轉(zhuǎn)眼之間累贤,2017春節(jié)將要過去了,過年因為種種原因沒有回家少漆,大年三十開始休息了三天臼膏,就投入了熱火朝天的工作之中,因...
    容若的小弟子閱讀 405評論 0 0