RxJava2 實戰知識梳理(4) - 結合 Retrofit 請求新聞資訊

RxJava2 實戰系列文章

RxJava2 實戰知識梳理(1) - 后臺執行耗時操作,實時通知 UI 更新
RxJava2 實戰知識梳理(2) - 計算一段時間內數據的平均值
RxJava2 實戰知識梳理(3) - 優化搜索聯想功能
RxJava2 實戰知識梳理(4) - 結合 Retrofit 請求新聞資訊
RxJava2 實戰知識梳理(5) - 簡單及進階的輪詢操作
RxJava2 實戰知識梳理(6) - 基于錯誤類型的重試請求
RxJava2 實戰知識梳理(7) - 基于 combineLatest 實現的輸入表單驗證
RxJava2 實戰知識梳理(8) - 使用 publish + merge 優化先加載緩存,再讀取網絡數據的請求過程
RxJava2 實戰知識梳理(9) - 使用 timer/interval/delay 實現任務調度
RxJava2 實戰知識梳理(10) - 屏幕旋轉導致 Activity 重建時恢復任務
RxJava2 實戰知識梳理(11) - 檢測網絡狀態并自動重試請求
RxJava2 實戰知識梳理(12) - 實戰講解 publish & replay & share & refCount & autoConnect
RxJava2 實戰知識梳理(13) - 如何使得錯誤發生時不自動停止訂閱關系
RxJava2 實戰知識梳理(14) - 在 token 過期時,刷新過期 token 并重新發起請求
RxJava2 實戰知識梳理(15) - 實現一個簡單的 MVP + RxJava + Retrofit 應用


一、前言

如何通過結合Retrofit框架來進行網絡請求,也是RxJava的學習過程中必須要掌握的一環。網上已經有很多開源項目和文章介紹了,今天這篇文章,我們就通過一個簡單的例子,通過RxJava + Retrofit的方式實現網絡請求。

這個例子很簡單,我們通過 干貨集中營 提供的接口,分別請求Android類和iOS類的資訊,并將這兩個接口所返回的數據在界面上進行展示。

通過該例子,可以學習如何將RetrofitRxJava結合,并通過zip操作符實現等待多個網絡請求完成。

二、示例

2.1 接口介紹

首先來熟悉一下所用到的測試接口,其數據來自于 干貨集中營,這里選擇AndroidiOS兩類的資訊,通過接口的描述,可以知道發起請求時的變量包含三個:

  • 分類
  • 請求個數
  • 請求頁數

返回的數據格式如下:


2.2 編寫 Entity 類

根據分析好的數據格式,我們編寫對應的Entity類:

  • 單次返回結果的數據結構:
public class NewsEntity {

    private boolean error;
    private List<NewsResultEntity> results = new ArrayList<>();

    public boolean isError() {
        return error;
    }

    public void setError(boolean error) {
        this.error = error;
    }

    public List<NewsResultEntity> getResults() {
        return results;
    }

    public void setResults(List<NewsResultEntity> results) {
        this.results = results;
    }
}
  • 單條資訊的數據結構:
public class NewsResultEntity {

    private String type;
    private String publishedAt;
    private String desc;
    private String who;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getPublishedAt() {
        return publishedAt;
    }

    public void setPublishedAt(String publishedAt) {
        this.publishedAt = publishedAt;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public String getWho() {
        return who;
    }

    public void setWho(String who) {
        this.who = who;
    }
}

2.3 引入 Retrofit 依賴

接下來,在build.gradle文件中,引入必要的依賴,以下三個依賴包的作用分別為:

  • Retrofit的核心庫
  • 將返回的Call<Response>轉換成Call<NewsEntity>
  • Call<NewsEntity>轉換成Observable<NewsEntity>
dependencies {
     //省略....
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    compile 'com.squareup.retrofit2:converter-gson:2.0.0'
    compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'

}

最后別忘了,在AndroidManifest.xml中聲明必要的網絡權限:

<uses-permission android:name="android.permission.INTERNET"/>

2.4 定義 Retrofit 需要的請求接口

按照Retrofit的使用介紹,我們需要定義一個接口類,這個接口類的返回值為Observable<NewsEntity>,也就是我們之前定義好的數據結構。而這個接口接收三個參數:請求類型、請求個數、請求所在頁數。

public interface NewsApi {

    @GET("api/data/{category}/{count}/{page}")
    Observable<NewsEntity> getNews(@Path("category") String category, @Path("count") int count, @Path("page") int page);
}

當我們需要請求數據時,就應當像下面這樣構造一個Observable<NewsEntity>

  • baseUrl:定義請求鏈接的前綴
  • addConverterFactory:將OKHttp返回的標準Response解析成我們所需要的數據類型NewsEntity
  • addCallAdapterFactory:將Call<NewsEntity>轉換成Observable<NewsEntity>,這樣才能真正將RetrofitRxJava結合起來。
    private Observable<NewsEntity> getObservable(String category, int page) {
        NewsApi api = new Retrofit.Builder()
                .baseUrl("http://gank.io")
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build().create(NewsApi.class);
        return api.getNews(category, 10, page);
    }

2.5 發起請求

以上就是所有的準備工作,回顧一下我們主要做了以下四步,這也是今后我們使用其它任意接口時的標準流程:

  • 熟悉接口
  • 根據接口返回的數據,定義Entity
  • 根據接口的url組成方式定義Retrofit所需要的接口聲明,接口函數的返回類型為Observable<Entity>,其中Entity就是第二步中定義好的返回數據類型。
  • 通過Retrofit,根據第三步的接口定義,返回真正的Observable

其實經過以上的四步,我們的工作就基本上完成了,只需要把上面第四步中返回的Observable<XXXEntity>當做一個發送數據的普通數據源就可以了。

示例代碼如下,我們請求了AndroidiOS兩個接口,并且使用zip操作符讓兩個接口都返回之后,才將數據呈現給用戶,同時每次點擊刷新資訊之后,我們將頁數增加一以請求新的資訊。

public class NewsActivity extends AppCompatActivity {

    private int mCurrentPage = 1;
    private NewsAdapter mNewsAdapter;
    private List<NewsResultEntity> mNewsResultEntities = new ArrayList<>();
    private CompositeDisposable mCompositeDisposable = new CompositeDisposable();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_news);
        initView();
    }

    private void initView() {
        Button btRefresh = (Button) findViewById(R.id.bt_refresh);
        btRefresh.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                refreshArticle(++mCurrentPage);
            }
        });
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv_news);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        mNewsAdapter = new NewsAdapter(mNewsResultEntities);
        recyclerView.setAdapter(mNewsAdapter);
        refreshArticle(++mCurrentPage);
    }

    private void refreshArticle(int page) {
       Observable<List<NewsResultEntity>> observable = Observable.just(page).subscribeOn(Schedulers.io()).flatMap(new Function<Integer, ObservableSource<List<NewsResultEntity>>>() {

            @Override
            public ObservableSource<List<NewsResultEntity>> apply(Integer page) throws Exception {
                Observable<NewsEntity> androidNews = getObservable("Android", page);
                Observable<NewsEntity> iosNews = getObservable("iOS", page);
                return Observable.zip(androidNews, iosNews, new BiFunction<NewsEntity, NewsEntity, List<NewsResultEntity>>() {

                    @Override
                    public List<NewsResultEntity> apply(NewsEntity androidEntity, NewsEntity iosEntity) throws Exception {
                        List<NewsResultEntity> result = new ArrayList<>();
                        result.addAll(androidEntity.getResults());
                        result.addAll(iosEntity.getResults());
                        return result;
                    }
                });
            }
        });
        DisposableObserver<List<NewsResultEntity>> disposable = new DisposableObserver<List<NewsResultEntity>>() {

            @Override
            public void onNext(List<NewsResultEntity> value) {
                mNewsResultEntities.clear();
                mNewsResultEntities.addAll(value);
                mNewsAdapter.notifyDataSetChanged();
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        };
        observable.observeOn(AndroidSchedulers.mainThread()).subscribe(disposable);
        mCompositeDisposable.add(disposable);
    }

    private Observable<NewsEntity> getObservable(String category, int page) {
        NewsApi api = new Retrofit.Builder()
                .baseUrl("http://gank.io")
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build().create(NewsApi.class);
        return api.getNews(category, 10, page);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mCompositeDisposable.clear();
    }
}

運行結果為:


三、示例解析

關于如何使用Retrofit + RxJava前面已經說得比較清楚了,下面我們重點介紹一下新接觸的兩個操作符,flatMapzip

3.1 flatMap

flatMap的原理圖如下所示:


它接收一個Function函數,對于上游發送的每個事件它都會應用該函數,這個函數返回一個新的Observable,如果有多個Observable,那么他會發送合并后的結果。

在上面的例子中,上游的just發送一個請求的所在頁數,我們根據這個頁數再去創建一個新的Observable來發送數據。

3.2 zip

zip操作符的原理圖如下所示:


它接收多個Observable,以及一個函數,該函數的形參為這些Observable發送的數據,并且要等所有的Observable都發射完會后才會回調該函數。

通過zip操作符,我們就可以實現等待多個網絡請求完成再返回的需求,例如在上面的例子中,我們會等待AndroidiOS類的資訊請求都返回之后,再合并它們的結果發送給下游,在界面上展示。


更多文章,歡迎訪問我的 Android 知識梳理系列:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容