最近在專案中使用到了
JakeWharton
大佬的RxBinding
,在某個表單驗證的模組中發現運用RxBinding
能得到很好的效果,既減小了程式碼量,邏輯也更清晰。感嘆它的神奇之處,於是檢視了下原始碼,發現原始碼並不是很複雜,於是決定跟著原始碼自己動手擼一個簡易版RxBinding
,加深對RxJava
的理解。
預計實現兩個目標:
- 將
View
的點選事件以RxJava
形式實現 - 將
TextView
以及子類
的的文字變化以RxJava
實現
先實現目標一。 大致思路:為view設定監聽器,當每次點選事件到來時,向下遊傳送事件。
我們看下原版RxBinding是怎麼實現點選事件訂閱的。
RxView.clicks(tv_xx).subscribe({
....dosomething()
})
複製程式碼
可以很清楚的知道,RxView.clicks(View view)
這個方法返回的是一個可被訂閱的observable
,我們看引數傳入了需要被訂閱點選事件view
的引用,可以猜想這個observable
和view
肯定有某種py,先不管。
依樣畫葫蘆,原版RxBinding
是通過靜態方法返回可被訂閱的的observable
,我們也照著寫就行了,本著學習的心態,不必在這些地方上糾結,重要的是擬清思路。
public class RxView {
/**
* 這裡肯定是返回一個可被訂閱的observable
* @param view 需要被訂閱點選事件的view
* @return
*/
public static Observable clicks(View view) {
return null;
}
}
複製程式碼
程式碼很簡單,就是一個靜態方法返回一個observable
,因為我們還沒寫對應的observable
,所以這裡暫時先返回null。
接下來就要寫我們的observable
了,首先我們的observable
肯定是和view
有著密不可分的關係的,所以肯定得持有view
的引用,所以先這樣寫:
新建一個observable
,命名為ViewClickObservable
,繼承自Observable
,並在構造中傳入view
,然後儲存.
public class ViewClickObservale extends Observable {
private View mView;
public ViewClickObservale(View mView) {
this.mView = mView;
}
/**
* 繼承Observable必須實現的抽象方法
* @param observer
*/
@Override
protected void subscribeActual(Observer observer) {
}
}
複製程式碼
我們看到程式碼中多了個subscribeActual(Observer observer)
方法,這是Observable
類中的抽象方法,必須重寫,該方法在observable
被下游訂閱時會被呼叫。而引數傳入的observer
,就是我們的發射器了。有的人可能這裡有疑問了,observer
不是觀察者嗎,怎麼又變成發射器了,發射事件不是被觀察者做的事情嗎?如果以以往的固定思維,到這裡是有點轉不過圈子。但是如果你深入過RxJava
,你會發現,它只不過是通過Java
語法的各種包裝,鏈式呼叫,形成了一種響應式風格而已.舉個例子,如果你平常使用RxJava
在Observer
的onNext()
方法中收到一個事件,你腦子中形成的模型肯定是上游發射了一個事件到下游,然後observe
r在onNext()
接受到了這個事件,可是本質上卻是內部呼叫了最上游的observer
的onNext()
方法而已,這裡之所以說最上游的observer
是因為中間可能用到各種操作符,而這些操作符起到的作用就是對最下游的observer
進行層層包裝改造,或者限制,最終傳到源頭的Observable
,然後用這個被包裝改造後的observer
發射事件,因為每個操作符的包裝改造效果不同,所以下游能收到不同的效果.
大致知道了原理,我們就可以繼續改造我們的ViewClickObservale
類了,我們知道了傳入的observer
為發射器,那麼事件從哪裡來呢?肯定還是隻能從點選事件了.每次收到一個點選事件,我們就向下遊發射一個事件就行了,繼續加程式碼.
public class ViewClickObservale extends Observable<Object> {
private View mView;
public ViewClickObservale(View mView) {
this.mView = mView;
}
/**
* 繼承Observable必須實現的抽象方法
* @param observer
*/
@Override
protected void subscribeActual(final Observer observer) {
//為view設定監聽,每次收到點選事件,向下遊發射事件
mView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
observer.onNext("onClick");
}
});
}
}
複製程式碼
這是編譯器提示未給Observer
指定泛型,可能存在隱患的安全問題.我第一次看到這個提示是這想的,既然是點選事件,下游不關心事件本身,傳送一個"onClick"
(String
型別)就行了,因為String
為Object
子類,所以泛型指定為這樣:
//泛型指定為Object子類
protected void subscribeActual(final Observer<? extend Object> observer)
複製程式碼
不料卻報錯了,看了下報錯原因是Observe
r強迫指定泛型為<? super T>
型別,然後我改成這樣就沒問題了.
protected void subscribeActual(final Observer<? super Object> observer)
複製程式碼
我感覺很奇怪,我想傳送事件的是String
型別,是Object
的子類,應該使用<? extend Object>
啊,然而使用<? super Object>
才是正確的。
思考了半天終於明白:
- 使用
<? extend Object>
表示型別為Object
以及子類,表示一個未知的型別,可能是Int
,也能是String
,編譯器不知道你具體指定的型別,所以不允許你傳送String
,因為你泛型也可以指定為Int或者其它. - 使用
<? super Object>
表示具體型別只能為Object
以及父類,因為Object
沒有父類,也就是隻能是Object
,Strin
g和Int
都是Object
子類,所以傳送String
或者Int
都是沒問題的,Object
型別的引用均能指向他們.
寫好了之後修改RxView類的程式碼
public class RxView {
/**
* 這裡肯定是返回一個可被訂閱的observable
*
* @param view 需要被訂閱點選事件的view
* @return
*/
public static Observable<Object> clicks(View view) {
return new ViewClickObservale(view);
}
}
複製程式碼
不再返回null
,返回我們剛寫好的Observable
.然後進行測試.
MainActivity中的程式碼:
public class MainActivity extends AppCompatActivity {
String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RxView.clicks(findViewById(R.id.tv_obs))
.subscribe(new Consumer<Object>() {
@Override
public void accept(Object o) throws Exception {
Log.d(TAG, "我被點選了");
}
});
}
}
複製程式碼
佈局中就一個TextView
,我點選了三次,列印出的日誌如下:
05-03 23:55:42.143 1770-1770/april.lesincs.rxbinding_demo D/MainActivity: 我被點選了
05-03 23:55:43.068 1770-1770/april.lesincs.rxbinding_demo D/MainActivity: 我被點選了
05-03 23:55:43.780 1770-1770/april.lesincs.rxbinding_demo D/MainActivity: 我被點選了
複製程式碼
測試成功!一個簡易版的RxBinding
就擼出來了,程式碼不多,但是需要對RxJava
有一定理解才能知道為啥要這麼寫,原版中還多了取消訂閱以及主執行緒檢測的功能,可以說是十分嚴謹了,我這裡就不繼續擼了,想了解的可以去看看原始碼.
下篇準備實現第二個目標:將TextView
以及子類
的的文字變化以RxJava
實現,敬請期待.