一、前言
如何通過結合Retrofit
框架來進行網路請求,也是RxJava
的學習過程中必須要掌握的一環。網上已經有很多開源專案和文章介紹了,今天這篇文章,我們就通過一個簡單的例子,通過RxJava + Retrofit
的方式實現網路請求。
這個例子很簡單,我們通過 乾貨集中營 提供的介面,分別請求Android
類和iOS
類的資訊,並將這兩個介面所返回的資料在介面上進行展示。
通過該例子,可以學習如何將Retrofit
和RxJava
結合,並通過zip
操作符實現等待多個網路請求完成。
二、示例
2.1 介面介紹
首先來熟悉一下所用到的測試介面,其資料來自於 乾貨集中營,這裡選擇Android
和iOS
兩類的資訊,通過介面的描述,可以知道發起請求時的變數包含三個:
- 分類
- 請求個數
- 請求頁數
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>
,這樣才能真正將Retrofit
和RxJava
結合起來。
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>
當做一個傳送資料的普通資料來源就可以了。
示例程式碼如下,我們請求了Android
和iOS
兩個介面,並且使用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
前面已經說得比較清楚了,下面我們重點介紹一下新接觸的兩個操作符,flatMap
和zip
。
3.1 flatMap
flatMap
的原理圖如下所示:
Function
函式,對於上游傳送的每個事件它都會應用該函式,這個函式返回一個新的Observable
,如果有多個Observable
,那麼他會傳送合併後的結果。
在上面的例子中,上游的just
傳送一個請求的所在頁數,我們根據這個頁數再去建立一個新的Observable
來傳送資料。
3.2 zip
zip
操作符的原理圖如下所示:
Observable
,以及一個函式,該函式的形參為這些Observable
傳送的資料,並且要等所有的Observable
都發射完會後才會回撥該函式。
通過zip
操作符,我們就可以實現等待多個網路請求完成再返回的需求,例如在上面的例子中,我們會等待Android
和iOS
類的資訊請求都返回之後,再合併它們的結果傳送給下游,在介面上展示。
更多文章,歡迎訪問我的 Android 知識梳理系列:
- Android 知識梳理目錄:www.jianshu.com/p/fd82d1899…
- 個人主頁:lizejun.cn
- 個人知識總結目錄:lizejun.cn/categories/