基于RxJava Retrofit的網(wǎng)絡(luò)框架(一)

基于RxJava Retrofit網(wǎng)絡(luò)框架的搭建

RxJava、Retrofit兩個(gè)第三方庫的優(yōu)勢(shì)

RxJava的使用場(chǎng)景

本文討論的如無特殊說明遏考,均指代rxjava2 和 retrofit2死相。
我們先討論一下rxJava引入的背景杨名。有一些應(yīng)用場(chǎng)景,特別是一些復(fù)雜的異步回調(diào)場(chǎng)景常潮,如果使用傳統(tǒng)的開發(fā)模式斥黑,會(huì)如何實(shí)現(xiàn)揖盘。
列舉一些場(chǎng)景:

  1. EditText: 根據(jù)輸入內(nèi)容自動(dòng)搜索時(shí),只有當(dāng)輸入間隔大于某個(gè)duration時(shí)锌奴,才觸發(fā)搜索兽狭。
    同時(shí)要根據(jù)輸入內(nèi)容,實(shí)時(shí)判斷其有效性鹿蜀。
    對(duì)輸入的內(nèi)容箕慧,實(shí)時(shí)做出改變(比如不能輸入數(shù)字,特殊字符等)
    http://www.reibang.com/p/9aaccd7bb600
  2. 聯(lián)合判斷
    http://www.reibang.com/p/88ce90240396
  3. 注冊(cè)--登錄--獲取用戶信息(注冊(cè)的一般流程) 獲取權(quán)限--獲取經(jīng)緯度--鑒權(quán)--生成訂單(生成訂單一般流程) 這樣一連串的網(wǎng)絡(luò)請(qǐng)求茴恰。
  4. 結(jié)合多個(gè)接口的數(shù)據(jù)颠焦,再更新UI;或者歷史記錄往枣、購(gòu)物車記錄等伐庭,需要合并本地緩存和網(wǎng)絡(luò)請(qǐng)求返回的數(shù)據(jù);
  5. 網(wǎng)絡(luò)請(qǐng)求前置條件token的獲取,token可以本地緩存分冈,本地緩存拿不到要去網(wǎng)絡(luò)獲取圾另。同時(shí)對(duì)于token拿不到時(shí)的網(wǎng)絡(luò)請(qǐng)求先等待,token獲取后將等待的請(qǐng)求一一發(fā)出丈秩。甚至token請(qǐng)求需要設(shè)置重試次數(shù)盯捌,超過次數(shù)才停止請(qǐng)求
  6. 防重復(fù)操作,倒計(jì)時(shí)
  7. 多重緩存處理:先讀取緩存更新UI蘑秽,在網(wǎng)絡(luò)請(qǐng)求更新UI(http://www.reibang.com/p/7474950af2df
  8. 多處場(chǎng)景需要同一個(gè)監(jiān)聽事件饺著,但是每個(gè)場(chǎng)景注冊(cè)時(shí)間點(diǎn)不同,如何做到事件產(chǎn)生之后注冊(cè)的監(jiān)聽者也能收到回調(diào)肠牲。

這些應(yīng)用場(chǎng)景幾乎是每個(gè)app都會(huì)遇到的幼衰,傳統(tǒng)的處理方式:層層遞進(jìn)(回調(diào)),各種標(biāo)志位控制缀雳,嵌套的if else渡嚣,各種異步調(diào)用分散在代碼各處,要將各個(gè)回調(diào)結(jié)果匯總肥印,并且綜合處理回調(diào)的不同狀態(tài)识椰。而且由于異步回調(diào)的特點(diǎn)深碱,代碼處理方式不會(huì)像同步調(diào)用那樣直接可控,異步回調(diào)導(dǎo)致的時(shí)序問題又將問題復(fù)雜度提高了幾個(gè)level敷硅。我經(jīng)常會(huì)想功咒,這種callback的異步調(diào)用方式,為何會(huì)將代碼變得丑陋不堪和難以維護(hù)力奋?設(shè)計(jì)模式中只提供了觀察者模式,但是對(duì)于觀察者回調(diào)復(fù)雜后景殷,沒有提出更好的解決方案。RxJava(或者說響應(yīng)式編程)主要解決了這類的問題滨彻。
關(guān)于RxJava是如何解決以上場(chǎng)景中遇到的問題藕届,以及如何和retrofit一起聯(lián)合使用休偶,短短幾行無法說不清,在基于RxJava Retrofit的網(wǎng)絡(luò)框架(二)中會(huì)詳解辜羊。

Retrofit的使用

Retrofit并不實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求本身(網(wǎng)絡(luò)請(qǐng)求由okhttp負(fù)責(zé)),他是一個(gè)框架八秃,為網(wǎng)絡(luò)請(qǐng)求本身提供可擴(kuò)展,可配置昔驱,易使用的外部封裝疹尾。okhttp負(fù)責(zé)提高網(wǎng)絡(luò)請(qǐng)求的性能和兼容性,Retrofit負(fù)責(zé)更好的使用他骤肛∧杀荆框架的重要性不言而喻,好的框架讓使用者關(guān)注更少細(xì)節(jié)腋颠,輕易的擴(kuò)展繁成。okhttp好像汽車的發(fā)動(dòng)機(jī),對(duì)于汽車性能和穩(wěn)定性起到?jīng)Q定性作用淑玫,框架就是除了發(fā)動(dòng)機(jī)以外其他部分巾腕,除了對(duì)發(fā)送機(jī)功能的整合外,漂亮的外觀絮蒿,舒適易用的體驗(yàn)才是人們?cè)敢忾_這臺(tái)車的原因尊搬。
以下列舉Retrofit相對(duì)于volley Android-Async-Http等框架的優(yōu)勢(shì)

  1. 簡(jiǎn)潔易用:通過注解配置網(wǎng)絡(luò)參數(shù),大量設(shè)計(jì)模式(建造者模式土涝,工廠)簡(jiǎn)化配置和使用毁嗦。
  2. 功能強(qiáng)大:支持RxJava方式(也支持callback方式),支持同步&異步回铛,
  3. 耦合度低狗准,擴(kuò)展性好:模塊高度封裝,徹底解耦茵肃。

由于Android-Async-Http的停更腔长,Google對(duì)volley基本放棄的態(tài)度,這兩套框架使用者和價(jià)值日漸減少验残。反觀okhttp捞附,Google官方應(yīng)用則廣泛使用。作為okhttp的黃金搭檔(同為square公司出品)您没,Retrofit可以說是目前Android app網(wǎng)絡(luò)請(qǐng)求框架的不二選擇鸟召,與okhttp搭配,是性能氨鹏、穩(wěn)定性欧募、兼容性、易用性都達(dá)到很高水平的框架組合仆抵。

Observable網(wǎng)絡(luò)框架的抽象

Observable網(wǎng)絡(luò)框架建立的原因

  1. Retrofit已經(jīng)對(duì)網(wǎng)絡(luò)請(qǐng)求做了封裝跟继,為什么還要封裝?
    網(wǎng)絡(luò)請(qǐng)求中對(duì)于請(qǐng)求流程、配置镣丑、加解密舔糖、異常處理對(duì)于每個(gè)app都是固定不變的,如果業(yè)務(wù)每次請(qǐng)求都自己處理邏輯莺匠,會(huì)存在冗余代碼秸脱,且質(zhì)量不易保證。所以我們需要基于Retrofit對(duì)請(qǐng)求流程侨赡、配置胆剧、加解密、異常處理等操作做二次封裝期贫,并對(duì)調(diào)用方式進(jìn)行統(tǒng)一。
  2. 框架封裝方式Observable是什么玛臂?
    對(duì)網(wǎng)絡(luò)請(qǐng)求二次封裝(一般為異步請(qǐng)求)封孙,傳統(tǒng)使用callback方式異步回調(diào)網(wǎng)絡(luò)請(qǐng)求結(jié)果。但是這種callback的方式泡徙,沒有利用到Retrofit的一大優(yōu)勢(shì)--rxjava調(diào)用膜蠢,所以我們要基于rxjava調(diào)用方式莉兰,封裝一個(gè)基于Observable的網(wǎng)絡(luò)請(qǐng)求框架糖荒。
    以下所說網(wǎng)絡(luò)框架模捂,均指基于Observable的網(wǎng)絡(luò)請(qǐng)求二次封裝框架。

Observable網(wǎng)絡(luò)框架要解決的問題

網(wǎng)絡(luò)框架要幫助業(yè)務(wù)處理以下幾個(gè)問題:

  1. 支持Get Post請(qǐng)求综看,對(duì)Request的參數(shù)業(yè)務(wù)可輕松配置
  2. 對(duì)Request 參數(shù)做發(fā)送前處理:組合和加密處理
  3. 返回Response 解密處理岖食,Java實(shí)體化
  4. 返回Response code碼判斷及處理
  5. 網(wǎng)絡(luò)請(qǐng)求cancle機(jī)制 progressBar配置等通用處理
    達(dá)到的目標(biāo):業(yè)務(wù)使用框架時(shí),只需要關(guān)注業(yè)務(wù)相關(guān)(Request參數(shù)句喷,Response返回值的配置和處理)兔毙,其他都交給框架處理。同時(shí)對(duì)于網(wǎng)絡(luò)請(qǐng)求的屬性可配置(error是否提示锡溯,progressBar是否顯示等)

Observable網(wǎng)絡(luò)框架如何實(shí)現(xiàn)

設(shè)計(jì)原則:

  1. 網(wǎng)絡(luò)請(qǐng)求Api返回Observable對(duì)象哑姚,作為網(wǎng)絡(luò)請(qǐng)求事件的生產(chǎn)者:
    生產(chǎn)者負(fù)責(zé)請(qǐng)求的發(fā)起叙量,和返回的所有預(yù)處理。
  2. 為業(yè)務(wù)提供BaseObserver類寺鸥,使用者實(shí)現(xiàn)其子類作為消費(fèi)者
    消費(fèi)者基類提供對(duì)response的一般處理品山,消費(fèi)者業(yè)務(wù)也可以使用自己Observer處理

在Observable的創(chuàng)建過程中,框架如何封裝笆载?

首先我們需要一個(gè)Manager或Helper全局實(shí)例,通過他可以發(fā)起網(wǎng)絡(luò)請(qǐng)求腻要,一般設(shè)計(jì)為單例全局持有沿侈,有利于網(wǎng)絡(luò)請(qǐng)求一些資源的共用。
我們暫定為NetHelper,其網(wǎng)絡(luò)請(qǐng)求接口定義為:

private static Observable<R> sendRequest(final HttpRequest request, final TypeReference<R> t)

sendRequest方法中,我們來看下Observable對(duì)象的生成過程:此處我們基于Retrofit本身Observable生成方式填帽,我們先看下Retrofit最基礎(chǔ)是如何創(chuàng)建Observable的。

第一步褐荷,定義Request嘹悼,Request類的定義在Retrofit里通過注解的方式完成的

public interface Request {

    @POST("{url}")
    Observable<JSONObject> postJSONResult(@Path(value="url",encoded =true) String url, @FieldMap Map<String, String> params);
}

可以看到我們的定義方式是通用的(每個(gè)request都可以復(fù)用),每個(gè)request都是通過postJSONResult方法獲取observable其监,傳入自己的url和params即可完成不同的網(wǎng)絡(luò)請(qǐng)求抖苦。

第二步,創(chuàng)建retrofit
NetHelper.java中

// 初始化okhttp
OkHttpClient client = new OkHttpClient.Builder()
        .build();

// 初始化Retrofit
retrofit = new Retrofit.Builder()
        .client(client)
        .baseUrl(Request.HOST)
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .addConverterFactory(MyConverterFactory.create())
        .build();

OkHttpClient每次請(qǐng)求的時(shí)候都要?jiǎng)?chuàng)建锌历,注意:OkHttpClient.Builder()中有ConnectionPool作為OkHttp的連接池要復(fù)用究西,否則請(qǐng)求過多時(shí)容易導(dǎo)致內(nèi)存溢出物喷。
創(chuàng)建Retrofit實(shí)例過程中,設(shè)置了okHttpClient商膊,baseUrl宠进,調(diào)用方式rxJava(通過addCallAdapterFactory)
GsonConverterFactory這些都是一般的寫法,GsonConverterFactory作用是把Response通過GSon轉(zhuǎn)為javaBean实幕。App業(yè)務(wù)中一般是先解密后Gson轉(zhuǎn),所以此處使用MyConverterFactory實(shí)現(xiàn)解密功能末贾。

第三步整吆,生成Observable
這一步是生成observable的過程,與httpRequest本身有關(guān)(我們前面提到了Request類是一個(gè)支持Retrofit通用類拴测,業(yè)務(wù)自定義的請(qǐng)求類HttpRequest實(shí)現(xiàn)了 getURLParam() getURLAction()等方法),所以這個(gè)獲取Observable的方法可以放到HttpRequest中進(jìn)行(NetHelper.sendRequest方法是傳入了HttpRequest對(duì)象的)

Request request = retrofit.create(Request.class);
return request.postJSONResult(getURLAction(),getURLParam());

對(duì)于httpRequest中入?yún)⒌慕M合和加密府蛇,實(shí)現(xiàn)在getURLParam()方法里。
備注:我們的網(wǎng)絡(luò)post請(qǐng)求params是query形式的务荆,如果是body表單穷遂,還需要另外的處理方式。

以上三步浦箱,已經(jīng)初步將Observable返回祠锣。通過以上幾步只是基于Retrofit自身的Observable創(chuàng)建方法做了一些封裝。下面的處理是框架的重點(diǎn)和核心:

private static Observable<R> sendRequest(final HttpRequest request蓬推,final TypeReference<R> t){

        return NetHelper.getApiObservable(request)
                .compose(ResponseTransformer.handleResult())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());

    }

注意:sendRequest方法一定要Observable所有的鏈?zhǔn)讲僮鲌?zhí)行完后在返回澡腾。


NetHelper.getApiObservable方法后,再加上網(wǎng)絡(luò)請(qǐng)求的線程配置毅糟,這時(shí)候業(yè)務(wù)subscribe消費(fèi)者澜公,就可以直接得到解密后的JsonObject了。注意此時(shí)是string而不是Retrofit通常的JavaBean迹辐,這是因?yàn)槲覀円x一個(gè)通用的Request類,將其接口返回值定義為了Observable<JSONObject>,所以我們還需要一步轉(zhuǎn)換间学。

    .map(new Function<JSONObject,R>() {
        @Override
        public R apply(JSONObject jsonObject) throws Exception {
            if (jsonObject != null){
                R response = jsonObject.toJavaObject(con);
                if (orgRequest != null) {
                    HttpHelper.printHttpLog(orgRequest, jsonObject.toString());
                }
                return response;
            } else {
                return null;
            }
        }
    })

response的異常處理低葫,progressbar的顯示等仍律,也需要架構(gòu)統(tǒng)一處理。我們引入了ResponseTransformer,你可以把他理解為map操作符主到,在交給消費(fèi)者前對(duì)response結(jié)果做了處理。

public static <T extends Serializable> ObservableTransformer<T, T> handleResult() {
    return upstream -> upstream
            .onErrorResumeNext(new ErrorResumeFunction<T>())
            .flatMap(new ResponseFunction<T>());
}

private static class ErrorResumeFunction<T extends Serializable> implements Function<Throwable, ObservableSource<? extends T>> {

    @Override
    public ObservableSource<? extends T> apply(Throwable throwable) throws Exception {
        return Observable.error(CustomException.handleException(throwable));
    }
}

private static class ResponseFunction<T extends Serializable> implements Function<T, ObservableSource<T>> {

    @Override
    public ObservableSource<T> apply(T tResponse) throws Exception {
        int code = tResponse.getCode();
        String message = tResponse.getMsg();

        if (code == SUCCESS.value()) {
            return Observable.just(tResponse);
        } else {
            return Observable.error(new ApiException(code, message));
        }
    }
}

可以看出對(duì)于事件流upstream登钥,做了正常和異常的再分流,對(duì)于服務(wù)器錯(cuò)誤(超時(shí)看锉,404等)通過onErrorResumeFunction繼續(xù)拋出Observable.error,
對(duì)于正常返回塔鳍,根據(jù)response中code的定義,只有SUCCESS時(shí)才返回?cái)?shù)據(jù)Observable.just(),其他情況(業(yè)務(wù)錯(cuò)誤等)作為錯(cuò)誤情況繼續(xù)拋出轮纫。
你可能有兩個(gè)疑問,一個(gè)是response中code的判定可以在observer中處理嗎放前,另一個(gè)是服務(wù)器錯(cuò)誤和業(yè)務(wù)錯(cuò)誤為何都作為error拋出糯彬。

第一個(gè)問題:
code值的判定不可以在observer中處理,而必須在Observable一端處理似扔。因?yàn)镺bservable形式的網(wǎng)絡(luò)請(qǐng)求是作為數(shù)據(jù)流中的一環(huán)出現(xiàn)的,可能當(dāng)前網(wǎng)絡(luò)請(qǐng)求只是一連串異步調(diào)用(rxjava調(diào)用)的一環(huán)锤灿。
第二個(gè)問題:
response中code!=SUCCESS是業(yè)務(wù)錯(cuò)誤的情況辆脸,必須向數(shù)據(jù)流中發(fā)出,讓業(yè)務(wù)處理此異常状囱。(那同時(shí)對(duì)于Response的定義也是倘是,code!=SUCCESS必須是不需要業(yè)務(wù)處理的情況才行)
兩種錯(cuò)誤都拋出error(內(nèi)部code不同),方便架構(gòu)使用者在事件響應(yīng)時(shí)叨粘,既能捕捉所有錯(cuò)誤瘤睹,又能區(qū)分錯(cuò)誤的類型。


哪些處理放到了BaseObserver中?

BaseObserver顧名思義驴党,是架構(gòu)使用者在rxjava流式調(diào)用最后一步所使用的觀察者基類获茬,他適合將網(wǎng)絡(luò)請(qǐng)求的UI響應(yīng)放入其中。

public  abstract class   BaseObserver<T > implements Observer<T>{
  /**
    * 請(qǐng)求成功
    * @param t
    */
   public abstract void onSuccess(T t);

   /**
    * 請(qǐng)求失敗
    * @param
    * @param object
    */
   public abstract void onFail(ApiException);

   @Override
    public void onSubscribe(Disposable d) {
        if (isShowProgress()) {
            showProgress(true);
        }
    }

    @Override
    public void onNext(T t) {
        if (isShowProgress()) {
            showProgress(false);
        }
        onSuccess(t);
    }

    @Override
    public void onError(Throwable e) {
      if (isShowProgress()) {
          showProgress(false);
      }

      if (e instanceof ApiException){
            ApiException apiException = (ApiException)e;
            switch(apiException.getCode()){
              case:NOT_LOGIN
                break;
              case:TOKEN_ERROR
                break;
            }
      }
    }

    /**
     * 網(wǎng)絡(luò)請(qǐng)求是否loading顯示
     * @return
     */
    protected boolean isShowProgress(){
        return true;
    }
}

BaseObserver中鹏氧,我們可以看到處理了showProgress度帮,ApiException做了處理稿存。同時(shí)可以對(duì)progressBar是否顯示做配置÷食幔可以滿足一般的網(wǎng)絡(luò)請(qǐng)求架構(gòu)使用袖迎,當(dāng)然也可以自行subscribe自己的Observer腺晾。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末悯蝉,一起剝皮案震驚了整個(gè)濱河市托慨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蕉世,老刑警劉巖婆硬,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件彬犯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蜜自,警方通過查閱死者的電腦和手機(jī)卢佣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門虚茶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仇参,“玉大人,你說我怎么就攤上這事罩扇∨履ィ” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵员帮,是天一觀的道長(zhǎng)导饲。 經(jīng)常有香客問我氯材,道長(zhǎng)氢哮,這世上最難降的妖魔是什么型檀? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮生闲,結(jié)果婚禮上月幌,老公的妹妹穿的比我還像新娘。我一直安慰自己扯躺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布倍啥。 她就那樣靜靜地躺著澎埠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪氮趋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天剩胁,我揣著相機(jī)與錄音祥国,去河邊找鬼。 笑死啊犬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的椒惨。 我是一名探鬼主播潮罪,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼沃暗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起孽锥,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎唬涧,沒想到半個(gè)月后盛撑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡狮荔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年殖氏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片雅采。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡总滩,死狀恐怖巡雨,靈堂內(nèi)的尸體忽然破棺而出席函,到底是詐尸還是另有隱情,我是刑警寧澤正蛙,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布营曼,位于F島的核電站,受9級(jí)特大地震影響锻全,放射性物質(zhì)發(fā)生泄漏狂塘。R本人自食惡果不足惜荞胡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一了嚎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧萝勤,春花似錦呐伞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舵抹。三九已至劣砍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間刑枝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工靠娱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留像云,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓迅诬,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親侈贷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子等脂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,304評(píng)論 25 707
  • 用兩張圖告訴你,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料嫁蛇? 從這篇文章中你...
    hw1212閱讀 12,745評(píng)論 2 59
  • 前言RxJava和Retrofit也火了一段時(shí)間了睬棚,不過最近一直在學(xué)習(xí)ReactNative和Node相關(guān)的姿勢(shì),...
    AFinalStone閱讀 555評(píng)論 0 0
  • Android 教你一步步搭建MVP+Retrofit+RxJava網(wǎng)絡(luò)請(qǐng)求框架 轉(zhuǎn)載請(qǐng)注明出處http://ww...
    milomallo閱讀 1,349評(píng)論 2 25
  • 讀完這本《上課紀(jì)》包警,其實(shí)心里有些小疙瘩害晦,并不很舒服,也并不愉悅壹瘟。大概因?yàn)槠饺绽镒约壕褪莻€(gè)對(duì)人對(duì)事不太敏感的青年鳄逾,對(duì)...
    波妞Martina閱讀 268評(píng)論 0 0