一、RxJava的核心思想
介紹RxJava的核心思想之前先來看一個例子
- 例子
在我們開發中,假如我們需要下載一張圖片,並且顯示出來,可以有多種方法,A可能使用new Thread+Handler來實現、B使用AsynTask來實現、C可能使用別的方式來實現。各自寫的代碼都不一樣,這樣就會造成下載圖片的一個功能,沒有統一的思維。
RxJava的思想就是基於起點到終點的一個模式,在起點中輸入一個事件,終點輸出我們想要的結果。在起點和終點之間可能會有多個攔截加工對事件的處理,但是目的都是爲了在終點輸出我們想要的結果。起點和終點的線路不會斷。
就好比生活中的例子,在工廠的車間的生產線中,總會有一個材料的輸入源頭,和成品的輸出結果。中間是一條流水線,在流水線中可能會有多道加工程序進行加工,當最後一道加工程序加工完之後,就會輸出一個成品。也就是我們終點的結果就是拿到離我們最近的加工程序的結果。
下面我們通過RxJava來實現一個下載圖片的功能:
String path = "http://39.108.14.94:9007/donghui_oa/IMG/20210103041020962378.jpg";
private void test1() {
//1、輸入一個事件path
Observable.just(path)
//2、對事件加工
.map(new Function<String, Bitmap>() {
@Override
public Bitmap apply(@NonNull String s) throws Exception {
URL url = new URL(path);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(16000);
int code = httpURLConnection.getResponseCode();
if (code == HttpURLConnection.HTTP_OK) {
InputStream inputStream = httpURLConnection.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
return bitmap;
}
return null;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
//3、訂閱
.subscribe(new Observer<Bitmap>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e(TAG, "onSubscribe: " );
}
//4、輸出事件
@Override
public void onNext(@NonNull Bitmap bitmap) {
Log.e(TAG, "onNext: " );
image.setImageBitmap(bitmap);
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG, "e: "+e );
}
@Override
public void onComplete() {
Log.e(TAG, "onComplete: " );
}
});
}
起點我們輸入一個path事件,接着對事件進行加工即通過path去網絡下載圖片,然後將事件類型轉換成bitmap。我們的終點接受上一道加工程序的事件bitmap。最後拿到結果顯示我們的圖片。
二、RxJava配合Retrofit的使用
在RxJava配合Retrofit的使用中,Retrofit負責去請求網絡拿到數據,並且將拿到的數據封裝成一個起點。接着通過RxJava反射數據將起點事件傳遞到終點。
- 例子
(1)封裝一個下載圖片的Api
package com.example.andoiddemo;
import io.reactivex.Observable;
import okhttp3.ResponseBody;
import retrofit2.http.GET;
import retrofit2.http.Streaming;
public interface Api {
@GET("IMG/20210103041020962378.jpg")
@Streaming
Observable<ResponseBody> downLoad();
}
(2)封裝Retrofit
package com.example.andoiddemo;
import android.util.Log;
import com.facebook.stetho.okhttp3.StethoInterceptor;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
public class RetrofitUtil {
final static String baseUrl = "http://39.108.14.94:9007/donghui_oa/";
static OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) { //訪問網絡請求,和服務端響應請求時。將數據攔截並輸出
Log.e("RetrofitUtil", "log: " + message);
}
}).setLevel(HttpLoggingInterceptor.Level.BODY)) //Log等級
.connectTimeout(16 ,TimeUnit.SECONDS) //超時時間
.readTimeout(16, TimeUnit.SECONDS)
.writeTimeout(16, TimeUnit.SECONDS)
.addNetworkInterceptor(new StethoInterceptor())
.build();
static Retrofit retrofit = new Retrofit
.Builder()
.baseUrl(baseUrl)
.client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create()).build();
public static Retrofit getInstance() {
return retrofit;
}
}
(3)下載圖片
//下載圖片
private void test2() {
//1、起點
Observable<ResponseBody> bodyObservable = RetrofitUtil.getInstance().create(Api.class).downLoad();
//2、起點發射事件
bodyObservable.
subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
//3、轉換事件類型
.map(new Function<ResponseBody, Bitmap>() {
@Override
public Bitmap apply(@NonNull ResponseBody responseBody) throws Exception {
Bitmap bitmap = BitmapFactory.decodeStream(responseBody.byteStream());
return bitmap;
}
})
//4、訂閱
.subscribe(new Observer<Bitmap>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e(TAG, "onSubscribe: ");
}
//5、接受事件
@Override
public void onNext(@NonNull Bitmap bitmap) {
Log.e(TAG, "onNext: " + bitmap);
image.setImageBitmap(bitmap);
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG, "onError: " + e);
}
@Override
public void onComplete() {
Log.e(TAG, "onComplete: ");
}
});
}
在Retrofit中支持添加一個addCallAdapterFactory爲RxJava2CallAdapterFactory,即我們定義API的時候可以定義返回RxJava的數據類型 Observable<ResponseBody>。這個即是我們在RxJava中說的起點。
接着訂閱Observable<ResponseBody>起點發射數據,並轉換數據成Bitmap。最後終點接受我們的Bitmap,拿到最後結果。顯示圖片。
三、防抖
在RxJava中封裝了一系列的庫,其中包含了對View操作的rxbinding。我們演示一個開發中經常遇到的例子,就是一個View在某個時間段內快速點擊了很多次,我們只響應一次。
private void test3() {
RxView.clicks(tvDown).throttleFirst(1000, TimeUnit.MILLISECONDS).subscribe(new Observer<Object>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull Object o) {
Log.e(TAG, "onNext: 響應事件....." );
}
@Override
public void onError(@NonNull Throwable e) {
}
@Override
public void onComplete() {
}
});
}
設置1S鍾內只響應一次。
四、網絡嵌套
在我們開發中經常會有這種需求,就是我們的業務是需要調用某個接口,然後拿到某個接口的數據再去調用某個接口,這樣是一個嵌套的網絡請求。當然還有可能嵌套很多層。就會給我們開發造成了比較大的麻煩。我們通過使用RxJava來實現一個網絡嵌套請求。
我們的業務是通過查詢一個分類列表接口,然後再通過分類裏面的ID 再查詢分類下的集合。
- 定義API
/**
* 查詢分類數據
* @return
*/
@GET("project/tree/json")
Observable<ProjectData> getProjectData();
/**
* 根據分類查詢列表數據
* @param page
* @param cid
* @return
*/
@GET("project/list/{page}/json?cid=294")
Observable<ItemData> getItemData(@Path("page") String page, @Query("cid") Integer cid)
- 封裝Retrofit
package com.example.andoiddemo;
import android.util.Log;
import com.facebook.stetho.okhttp3.StethoInterceptor;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
public class RetrofitUtil {
final static String baseUrl = "https://www.wanandroid.com/";
static OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) { //訪問網絡請求,和服務端響應請求時。將數據攔截並輸出
Log.e("RetrofitUtil", "log: " + message);
}
}).setLevel(HttpLoggingInterceptor.Level.BODY)) //Log等級
.connectTimeout(16 ,TimeUnit.SECONDS) //超時時間
.readTimeout(16, TimeUnit.SECONDS)
.writeTimeout(16, TimeUnit.SECONDS)
.addNetworkInterceptor(new StethoInterceptor())
.build();
static Retrofit retrofit = new Retrofit
.Builder()
.baseUrl(baseUrl)
.client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create()).build();
public static Retrofit getInstance() {
return retrofit;
}
}
- 封裝實體數據
package com.example.andoiddemo;
import java.util.List;
public class ProjectData {
private List<Data> data;
private int errorCode;
private String errorMsg;
public void setData(List<Data> data) {
this.data = data;
}
public List<Data> getData() {
return this.data;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public int getErrorCode() {
return this.errorCode;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public String getErrorMsg() {
return this.errorMsg;
}
static class Data {
private int courseId;
private int id;
private String name;
private int order;
private int parentChapterId;
private boolean userControlSetTop;
private int visible;
public void setCourseId(int courseId) {
this.courseId = courseId;
}
public int getCourseId() {
return this.courseId;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setOrder(int order) {
this.order = order;
}
public int getOrder() {
return this.order;
}
public void setParentChapterId(int parentChapterId) {
this.parentChapterId = parentChapterId;
}
public int getParentChapterId() {
return this.parentChapterId;
}
public void setUserControlSetTop(boolean userControlSetTop) {
this.userControlSetTop = userControlSetTop;
}
public boolean getUserControlSetTop() {
return this.userControlSetTop;
}
public void setVisible(int visible) {
this.visible = visible;
}
public int getVisible() {
return this.visible;
}
}
}
package com.example.andoiddemo;
import java.util.List;
public class ItemData {
private Data data;
private int errorCode;
private String errorMsg;
public void setData(Data data) {
this.data = data;
}
public Data getData() {
return this.data;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public int getErrorCode() {
return this.errorCode;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public String getErrorMsg() {
return this.errorMsg;
}
static class Datas {
private String apkLink;
private int audit;
private String author;
private boolean canEdit;
private int chapterId;
private String chapterName;
private boolean collect;
private int courseId;
private String desc;
private String descMd;
private String envelopePic;
private boolean fresh;
private String host;
private int id;
private String link;
private String niceDate;
private String niceShareDate;
private String origin;
private String prefix;
private String projectLink;
private String publishTime;
private String realSuperChapterId;
private String selfVisible;
private String shareDate;
private String shareUser;
private String superChapterId;
private String superChapterName;
private String title;
private int type;
private int userId;
private int visible;
private int zan;
public String getApkLink() {
return apkLink;
}
public void setApkLink(String apkLink) {
this.apkLink = apkLink;
}
public int getAudit() {
return audit;
}
public void setAudit(int audit) {
this.audit = audit;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public boolean isCanEdit() {
return canEdit;
}
public void setCanEdit(boolean canEdit) {
this.canEdit = canEdit;
}
public int getChapterId() {
return chapterId;
}
public void setChapterId(int chapterId) {
this.chapterId = chapterId;
}
public String getChapterName() {
return chapterName;
}
public void setChapterName(String chapterName) {
this.chapterName = chapterName;
}
public boolean isCollect() {
return collect;
}
public void setCollect(boolean collect) {
this.collect = collect;
}
public int getCourseId() {
return courseId;
}
public void setCourseId(int courseId) {
this.courseId = courseId;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getDescMd() {
return descMd;
}
public void setDescMd(String descMd) {
this.descMd = descMd;
}
public String getEnvelopePic() {
return envelopePic;
}
public void setEnvelopePic(String envelopePic) {
this.envelopePic = envelopePic;
}
public boolean isFresh() {
return fresh;
}
public void setFresh(boolean fresh) {
this.fresh = fresh;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public String getNiceDate() {
return niceDate;
}
public void setNiceDate(String niceDate) {
this.niceDate = niceDate;
}
public String getNiceShareDate() {
return niceShareDate;
}
public void setNiceShareDate(String niceShareDate) {
this.niceShareDate = niceShareDate;
}
public String getOrigin() {
return origin;
}
public void setOrigin(String origin) {
this.origin = origin;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getProjectLink() {
return projectLink;
}
public void setProjectLink(String projectLink) {
this.projectLink = projectLink;
}
public String getPublishTime() {
return publishTime;
}
public void setPublishTime(String publishTime) {
this.publishTime = publishTime;
}
public String getRealSuperChapterId() {
return realSuperChapterId;
}
public void setRealSuperChapterId(String realSuperChapterId) {
this.realSuperChapterId = realSuperChapterId;
}
public String getSelfVisible() {
return selfVisible;
}
public void setSelfVisible(String selfVisible) {
this.selfVisible = selfVisible;
}
public String getShareDate() {
return shareDate;
}
public void setShareDate(String shareDate) {
this.shareDate = shareDate;
}
public String getShareUser() {
return shareUser;
}
public void setShareUser(String shareUser) {
this.shareUser = shareUser;
}
public String getSuperChapterId() {
return superChapterId;
}
public void setSuperChapterId(String superChapterId) {
this.superChapterId = superChapterId;
}
public String getSuperChapterName() {
return superChapterName;
}
public void setSuperChapterName(String superChapterName) {
this.superChapterName = superChapterName;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getVisible() {
return visible;
}
public void setVisible(int visible) {
this.visible = visible;
}
public int getZan() {
return zan;
}
public void setZan(int zan) {
this.zan = zan;
}
}
static class Data {
private int curPage;
private List<Datas> datas;
private int offset;
private boolean over;
private int pageCount;
private int size;
private int total;
public void setCurPage(int curPage) {
this.curPage = curPage;
}
public int getCurPage() {
return this.curPage;
}
public void setDatas(List<Datas> datas) {
this.datas = datas;
}
public List<Datas> getDatas() {
return this.datas;
}
public void setOffset(int offset) {
this.offset = offset;
}
public int getOffset() {
return this.offset;
}
public void setOver(boolean over) {
this.over = over;
}
public boolean getOver() {
return this.over;
}
public void setPageCount(int pageCount) {
this.pageCount = pageCount;
}
public int getPageCount() {
return this.pageCount;
}
public void setSize(int size) {
this.size = size;
}
public int getSize() {
return this.size;
}
public void setTotal(int total) {
this.total = total;
}
public int getTotal() {
return this.total;
}
}
}
使用RxJava實現網絡嵌套請求
/**
* 查詢分類的列表,
* 再根據分類id查詢分類下的集合
*/
private void test4() {
RetrofitUtil.getInstance().create(Api.class).getProjectData()
.flatMap(new Function<ProjectData, ObservableSource<ProjectData.Data>>() {
@Override
public ObservableSource<ProjectData.Data> apply(@NonNull ProjectData projectData) throws Exception {
return Observable.fromIterable(projectData.getData());
}
})
.flatMap(new Function<ProjectData.Data, ObservableSource<ItemData>>() {
@Override
public ObservableSource<ItemData> apply(@NonNull ProjectData.Data data) throws Exception {
return RetrofitUtil.getInstance().create(Api.class).getItemData("1", data.getId());
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ItemData>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e(TAG, "onSubscribe: ");
}
@Override
public void onNext(@NonNull ItemData itemData) {
Log.e(TAG, "onNext: "+itemData.getData().getDatas() );
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG, "onError: "+e);
}
@Override
public void onComplete() {
Log.e(TAG, "onComplete: ");
}
});
}
五、doOnNext的運用
doOnNext操作符,允許我們從起點發射事件到終點期間對事件做一些額外的處理,然後繼續發射。例如我們發射一個長度爲3的集合,在發送期間,我們先對這個集合做一個校驗,然後繼續發射
private void test6() {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
Observable.just(list)
.doOnNext(new Consumer<List<String>>() {
@Override
public void accept(List<String> strings) throws Exception {
check(list);
}
}).flatMap(new Function<List<String>, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(@NonNull List<String> strings) throws Exception {
return Observable.fromIterable(strings);
}
}).subscribe(new Observer<String>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull String s) {
Log.e(TAG, "onNext: "+s );
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG, "onError: "+e);
}
@Override
public void onComplete() {
}
});
}
private void check(List<String> list) {
list.remove(0);
}
2021-06-08 16:49:21.485 26310-26310/com.example.andoiddemo E/MainActivity: onNext: 2
2021-06-08 16:49:21.485 26310-26310/com.example.andoiddemo E/MainActivity: onNext: 3
六、小結
以上我們介紹了RxJava的核心思想,RxJava的主要核心思想就是基於起點和終點的模式,每一個事件從起點輸入到終點結束,起點和終點之間可以做相應的攔截處理。最後一道攔截處理的結果就是終點的結果。
又介紹了一些平時常用的開發場景防抖、配合Retrofit的使用、優雅的解決網絡嵌套問題,以及doOnNext操作符的介紹。以上還使用到了map和flatmap操作符,他們都是可以對事件發射期間變換處理,然後發射變換之後的數據。map發射的數據是我們的具體的對象,而flatmap發射的數據還是Observable包裝後的對象,flatmap可以發射多次。例如起點輸入的是一個集合,可以通過flatmap發射多次,發射集合裏的每一個元素。