RxJava2.x的整合及用法詳解
阿新 • • 發佈:2018-12-31
目錄
主頁
中文資料
Rxjava是什麼
首先要了解什麼是觀察者
Android 開發中一個比較典型的例子是點選監聽器 OnClickListener 。對設定 OnClickListener 來說, View 是被觀察者, OnClickListener 是觀察者,二者通過 setOnClickListener() 方法達成訂閱關係。訂閱之後使用者點選按鈕的瞬間,Android Framework 就會將點選事件傳送給已經註冊的 OnClickListener 。採取這樣被動的觀察方式,既省去了反覆檢索狀態的資源消耗,也能夠得到最高的反饋速度。
如圖所示,通過 setOnClickListener() 方法,Button 持有 OnClickListener 的引用(這一過程沒有在圖上畫出);當用戶點選時,Button 自動呼叫 OnClickListener 的 onClick() 方法。另外,如果把這張圖中的概念抽象出來(Button -> 被觀察者、OnClickListener -> 觀察者、setOnClickListener() -> 訂閱,onClick() -> 事件),就由專用的觀察者模式(例如只用於監聽控制元件點選)轉變成了通用的觀察者模式。如下圖:
用途
- 非同步操作
- 在邏輯複雜的情況下, 仍然可以讓程式碼邏輯保持簡潔
配置 新增依賴即可
在com.android.tools.build:gradle 3.0 以上版本依賴在gradle 中的宣告寫法
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation 'io.reactivex.rxjava2:rxjava:2.x.y'
在com.android.tools.build:gradle 3.0 以下版本依賴在gradle 中的宣告寫法
compile 'io.reactivex.rxjava2:rxandroid:2.0.2'
compile 'io.reactivex.rxjava2:rxjava:2.x.y'
基本概念
1. 被觀察者. Observable
- 作用:決定什麼時候出發時間以及出發怎樣的事件
- 建立方法:
Observable.just (T t) //引數為單個的
Observable.flapMap() //引數為多個的
Observable.from (T [] t) //引數為陣列
Observable.from (Iterable <? extends T>) //引數為Iterable (ArrayList的一種實現)
2. 觀察者. Observer
- 作用:當事件觸發的時候將有怎樣的行為
- 實現類:
Observer \ Subscriber
3. 訂閱. Subscribe
- 作用:使被觀察者與觀察者建立聯絡
-方法:
observable.subscribe(observer);
observable.subscribe(subscriber);
4. 事件.
onNext():普通事件
onCompleted():時間佇列完結
onError():時間佇列異常
注意:onCompleted()與onError()是互斥的,呼叫了其中一個另一個就不會被觸發
5. Scheduler.
- 作用:控制執行緒,指定某一段程式碼在哪個執行緒執行
- 內建的Scheduler
Schedulers.newThread(); //總是啟用新執行緒,並在新的執行緒中執行操作
Schedulers.io(); //I/O操作(讀寫檔案、讀寫資料庫、網路請求)所使用的執行緒。行為模式和newThread差不多,區別在於IO的內部實現的是一個無數量上線的執行緒池,可以重用空閒執行緒,因此多數情況下IO()比newThread()更有效率,不要把計算工作放在IO中,可以避免不必要的建立執行緒
Schedulers.computation(); //計算使用的Schedulers,這個計算指的是CPU的密集型計算,既不會被I/O 等操作限制性能操作,例如圖形的計算,這個Scheduler使用的固定的執行緒池,大小為CPU的核數。不要把IO操作放在computation()中,否則IO操作等待時間會浪費CPU.
Schedulers.trampoline(); //排程器會將任務按新增順序依次放入當前執行緒中等待執行(當前執行緒就是Schedulers.trampoline排程器建立Worker物件後,Worker物件呼叫scedule方法所在的執行緒)。執行緒一次只執行一個任務,其餘任務排隊等待,一個任務都執行完成後再開始下一個任務的執行。
Schedulers.single(); //擁有一個執行緒單例,所有的任務都在這一個執行緒中執行,當此執行緒中有任務執行時,其他任務將會按照先進先出的順序依次執行。
Scheduler.from(@NonNull Executor executor); //指定一個執行緒排程器,由此排程器來控制任務的執行策略。
AndroidSchedulers.mainThread(); //在Android UI執行緒中執行任務,為Android開發定製。
Observable.subscribeOn(); //指subscribe() 所發生的執行緒,即Observable.onSubscribe被啟用時所處的執行緒。或者叫做事件產生的執行緒。
//若多次設定,則只有一次起作用。
Observable.observeOn(); //指Subscriber所執行在的執行緒,或者叫做事件的消費執行緒。(可以理解為現在要去渲染頁面了,需要到主執行緒中去)
//若多次設定,每次均起作用。
- 案例1
基本概念1-4應用
遍歷陣列,找出陣列中b開頭的元素,將其轉換為大寫
sample1:fori實現
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
String[] arr = {"shanghai", "beijing", "chengdu"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (int i = 0; i < arr.length ; i++) {
if (arr[i].startsWith("b")){
arr[i] = arr[i].toUpperCase();
Log.i(TAG,arr[i]);
}
}
}
}
輸出結果:06-30 10:48:02.487 747-747/com.kwunyamshan.rxjava I/MainActivity: BEIJING
sample2:RxJava實現
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
String[] arr = {"shanghai", "beijing", "chengdu"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//導包不要導錯 import io.reactivex.Observable;
Observable<String> observable = Observable.fromArray(arr);
//新增一個過濾器 , 把字母b開頭的元素過濾出來
Observable<String> filterObservable = observable.filter(new Predicate<String>() {
@Override
public boolean test(String s) throws Exception {
return s.startsWith("b");
}
});
//接收結果
Observer<String> observer = new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {}
//真正處理事件的方法
@Override
public void onNext(String s) {
s = s.toUpperCase();
Log.i(TAG, s);
}
@Override
public void onError(Throwable e) {}
@Override
public void onComplete() {}
};
//關聯觀察者與被觀察者
filterObservable.subscribe(observer);
}
輸出結果:06-30 10:49:00.371 3261-3261/com.kwunyamshan.rxjava I/MainActivity: BEIJING
sample3:RxJava實現(優化)
看起來sample1中的程式碼比sample2中的程式碼還要簡潔,脾氣暴躁的大哥就要來幹我了,別急一步一步來你就明
白了,下面是對RXJava進行了優化
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
String[] arr = {"shanghai", "beijing", "chengdu"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Observable.fromArray(arr)
////filter過濾以後返回的型別也是Observable, 所以變數可以刪去,鏈式程式設計
//Observable<String> filterObservable = observable.filter(new Predicate<String>() {
.filter(new Predicate<String>() {
@Override
public boolean test(String s) throws Exception {
return s.startsWith("b");
}
})
//這裡如果需要做一件事則不需要new Observer,
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
s = s.toUpperCase();
Log.i(TAG, s);
}
});
}
輸出結果:06-30 10:51:00.789 3261-3261/com.kwunyamshan.rxjava I/MainActivity: BEIJING
你會發現這樣寫邏輯條理會變得特別清楚(如果會用lambda表示式的話則會更加的簡潔),一個方法做一件
事情,再加多少個方法邏輯也不會亂,這樣寫不管過了多久再回過頭來看這段程式碼是不是也不迷糊了
- 案例2
基本概念1-5應用
獲取圖片,展示到ImageView上,出現異常時列印toast報錯(涉及到切換執行緒)
sample1:不考慮執行緒問題,簡單實現1對1資料轉換(關鍵字just)
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
private ImageView ivAvatar;
private int imgId = R.mipmap.avatar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ivAvatar = findViewById(R.id.iv_avatar);
//當物件是單個的時候呼叫just
Observable.just(imgId)
//將圖片id轉化為drawable
.map(new Function<Integer, Drawable>() {
@Override
public Drawable apply(Integer integer) throws Exception {
return getResources().getDrawable(imgId);
}
})
//把drawable設定到ImageView上
.subscribe(new Consumer<Drawable>() {
@Override
public void accept(Drawable drawable) throws Exception {
ivAvatar.setImageDrawable(drawable);
}
});
}
sample2:增加執行緒切換,1對1資料轉換(關鍵字just)不瞭解執行緒切換的翻回去看 5. Scheduler
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
private ImageView ivAvatar;
private int imgId = R.mipmap.avatar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ivAvatar = findViewById(R.id.iv_avatar);
//當物件是單個的時候呼叫just
Observable
.just(imgId)
//將圖片id轉化為drawable, 比方這個地方走網路請求圖片的話這樣肯定是走不成了
//所以我們需要在這裡新增切換執行緒的方法
.subscribeOn(Schedulers.io())
//以下程式碼在子執行緒中執行
.map(new Function<Integer, Drawable>() {
@Override
public Drawable apply(Integer integer) throws Exception {
return getResources().getDrawable(imgId);
}
})
//把drawable設定到ImageView上 , 渲染頁面的邏輯在主執行緒中呼叫
.observeOn(AndroidSchedulers.mainThread())
//以下程式碼在主執行緒中執行
.subscribe(new Observer<Drawable>() {
@Override
public void onSubscribe(Disposable d) {}
@Override
public void onNext(Drawable drawable) {
ivAvatar.setImageDrawable(drawable);
}
@Override
public void onError(Throwable e) {
Toast.makeText(MainActivity.this,e.getMessage(),1).show();
}
@Override
public void onComplete() {}
});
}
- 案例3
基本概念1-5應用
現在有兩張表,一張單位職工表,一張職工工資表,根據職工姓名(職工表)查詢職工每個月的工資(工資表)
sample1:fori實現1對多查詢
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SalaryBean jan = new SalaryBean("3000", 8);
SalaryBean feb = new SalaryBean("3400", 9);
SalaryBean mar = new SalaryBean("3000", 10);
SalaryBean apr = new SalaryBean("3400", 11);
SalaryBean may = new SalaryBean("5500", 12);
List<SalaryBean> salaryList = new ArrayList<>();
salaryList.add(jan);
salaryList.add(feb);
salaryList.add(mar);
salaryList.add(apr);
salaryList.add(may);
StaffBean 禿頭李 = new StaffBean("李老闆", salaryList);
for (int i = 0; i < 禿頭李.getmSalaryList().size(); i++) {
SalaryBean salaryBean = 禿頭李.getmSalaryList().get(i);
Log.i(TAG, "您" + salaryBean.getMonth() + "月份" + "的薪水:" + salaryBean.getSalary());
}
}
}
輸出結果:
06-30 10:53:48.641 8985-8985/com.kwunyamshan.rxjava I/MainActivity: 您8月份的薪水:3000
06-30 10:53:48.641 8985-8985/com.kwunyamshan.rxjava I/MainActivity: 您9月份的薪水:3400
06-30 10:53:48.641 8985-8985/com.kwunyamshan.rxjava I/MainActivity: 您10月份的薪水:3000
06-30 10:53:48.641 8985-8985/com.kwunyamshan.rxjava I/MainActivity: 您11月份的薪水:3400
06-30 10:53:48.642 8985-8985/com.kwunyamshan.rxjava I/MainActivity: 您12月份的薪水:5500
sample2:用RxJava實現1對多查詢(關鍵字flatMap)
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SalaryBean jan = new SalaryBean("3000", 8);
SalaryBean feb = new SalaryBean("3400", 9);
SalaryBean mar = new SalaryBean("3000", 10);
SalaryBean apr = new SalaryBean("3400", 11);
SalaryBean may = new SalaryBean("5500", 12);
List<SalaryBean> salaryList = new ArrayList<>();
salaryList.add(jan);
salaryList.add(feb);
salaryList.add(mar);
salaryList.add(apr);
salaryList.add(may);
StaffBean 禿頭李 = new StaffBean("李老闆", salaryList);
//查詢單個的員工用just
Observable
.just(禿頭李)
//查詢員工每個月工資情況用flatMap
.flatMap(new Function<StaffBean, Observable<SalaryBean>>() {
@Override
public Observable<SalaryBean> apply(StaffBean staffBean) throws Exception {
return Observable.fromIterable(staffBean.getmSalaryList());
}
})
.subscribe(new Consumer<SalaryBean>() {
@Override
public void accept(SalaryBean salaryBeans) throws Exception {
Log.i(TAG, "您" + salaryBeans.getMonth() + "月份" + "的薪水:" + salaryBeans.getSalary());
}
});
}
輸出結果:
06-30 10:54:16.278 10183-10183/com.kwunyamshan.rxjava I/MainActivity: 您8月份的薪水:3000
06-30 10:54:16.278 10183-10183/com.kwunyamshan.rxjava I/MainActivity: 您9月份的薪水:3400
06-30 10:54:16.278 10183-10183/com.kwunyamshan.rxjava I/MainActivity: 您10月份的薪水:3000
06-30 10:54:16.278 10183-10183/com.kwunyamshan.rxjava I/MainActivity: 您11月份的薪水:3400
06-30 10:54:16.278 10183-10183/com.kwunyamshan.rxjava I/MainActivity: 您12月份的薪水:5500