Android_RxJava代替EventBus實現事件分發
歡迎加入技術談論群:714476794
一、背景
眾所周知,RxJava目前很火爆,既然RxJava是響應式的典型觀察者模式體現,那麼問題來了,我可不可以用RxJava實現頁面事件的通知,進而實現代替EventBus。這當然是可以實現的,下面開擼:
(一)、RxBus的建立
最開始的寫法:
RxBus
/** * Author KINCAI * . * description TODO * . * Time 2016-12-07 12:54 */ public class RxBus { private final Subject<Object, Object> rxBus = new SerializedSubject<>(PublishSubject.create()); private Map<String,Subscription> mSubscriptions = new HashMap<>(); private RxBus() { } public static RxBus getInstance() { return RxBusInstance.instance; } private static class RxBusInstance{ private static final RxBus instance = new RxBus(); } public void send(EventBase eventBase){ rxBus.onNext(eventBase); } public void register(final Object obj, final EventListener listener){ if(obj == null) return; rxBus.observeOn(AndroidSchedulers.mainThread()) .map(new Func1<Object, EventBase>() { @Override public EventBase call(Object s) { return (EventBase) s; } }).subscribe(new Action1<EventBase>() { @Override public void call(EventBase eventBase) { listener.onEvent(eventBase); } }); } public interface EventListener { void onEvent(EventBase eventBase); } }
EventBase實體類
/** * Author KINCAI * . * description TODO * . * Time 2016-12-07 16:11 */ public class EventBase { private String tag; private String flag; private Object data; public EventBase(String tag, Object data) { this.tag = tag; this.data = data; } public EventBase(String tag, String flag, Object data) { this.tag = tag; this.flag = flag; this.data = data; } public EventBase() { } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } public String getFlag() { return flag; } public void setFlag(String flag) { this.flag = flag; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } @Override public String toString() { return "EventBase{" + "tag='" + tag + '\'' + ", flag='" + flag + '\'' + ", data=" + data + '}'; } }
傳送訊息
RxBus.getInstance().send(new EventBase(“new”,“new1”,"news"));
訂閱者
RxBuss.getInstance().register(this, new RxBuss.EventListener() {
@Override
public void onEvent(EventBase eventBase) {
LogUtils.e(eventBase.getTag());
}
});
新建RxBus類,宣告Subjcet<Object,Object> = new SerializedSubject<>(PublishSubject.create());
說明一下Subject可以看做是一個橋樑或者代理,充當了Observer和Observable的角色,也就是觀察者與被觀察者。
SerializedSubject可以處理併發,併發時只允許一個執行緒呼叫onnext等方法!。
PublishSubject是Subject的一種,它僅會向Observer釋放在訂閱之後Observable釋放的資料。
EventBase類是訊息實體類,可以看到上面所寫的就是通過send發訊息呼叫onNext,接收到訊息用訂閱者所在類的介面回撥過去,實現接收到訊息。
體驗了一把發現有問題,比如我在activity1 register了並跳轉到activity2同也register監聽,然後跳轉到activity3,銷燬activity2 並在activity3 send訊息 這是很會發現activity1和activity2都會受到訊息,明明activity被銷燬了,這怎麼回事?第二個問題就是,也不算問題 就是我想用類似EventBus註解方式接受訊息。
解決辦法:1、在activity 銷燬時 RxBus進行反註冊,這樣就需要在RxBus定義 private Map<String,Subscription> mSubscriptions = new HashMap<>();存放註冊的時候Subject subscribe返回的Subscription例項 可以通過Subscription取消訂閱。2、使用自定義註解
首先上程式碼:
Event註解
/**
* Author KINCAI
* .
* description TODO
* .
* Time 2016-12-08 10:21
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Event {
String value();
}
RxBus改進
**
* Author KINCAI
* .
* description TODO
* .
* Time 2016-12-07 12:54
*/
public class RxBus {
private final Subject<Object, Object>
rxBus = new SerializedSubject<>(PublishSubject.create());
private Map<String,Subscription> mSubscriptions = new HashMap<>();
private RxBus() {
}
public static RxBus getInstance() {
return RxBusInstance.instance;
}
private static class RxBusInstance{
private static final RxBus instance = new RxBus();
}
public void send(EventBase eventBase){
rxBus.onNext(eventBase);
}
public void register(final Object obj){
if(obj == null)
return;
Subscription subscription = rxBus.observeOn(AndroidSchedulers.mainThread())
.map(new Func1<Object, EventBase>() {
@Override
public EventBase call(Object s) {
return (EventBase) s;
}
}).subscribe(new Action1<EventBase>() {
@Override
public void call(EventBase eventBase) {
RxBus.this.call(obj, eventBase);
}
});
putSubscription(obj, subscription);
}
private void call(Object obj, EventBase eventBase){
Class<?> cls = obj.getClass();
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(Event.class)) {
Event event = method.getAnnotation(Event.class);
String value = event.value();
String tag = eventBase.getTag();
try {
if (TextUtils.isEmpty(tag) || TextUtils.isEmpty(value)) {
method.setAccessible(true);
method.invoke(obj, eventBase);
} else {
if (tag.equals(value)) {
method.setAccessible(true);
method.invoke(obj, eventBase);
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
public void unRegister(Object obj){
if(obj == null)
return;
Subscription subscription = getSubscription(obj);
if(subscription != null && !subscription.isUnsubscribed()){
subscription.unsubscribe();
}
}
private void putSubscription(Object obj,Subscription subscription){
String className = obj.getClass().getName();
for (Map.Entry<String, Subscription> entry : mSubscriptions.entrySet()) {
if(className.equals(entry.getKey())){
if(!entry.getValue().isUnsubscribed()){
entry.getValue().unsubscribe();
}
mSubscriptions.remove(className);
break;
}
}
mSubscriptions.put(className,subscription);
}
private Subscription getSubscription(Object obj){
String className = obj.getClass().getName();
for (Map.Entry<String, Subscription> entry : mSubscriptions.entrySet()) {
if(className.equals(entry.getKey())){
Subscription value = entry.getValue();
mSubscriptions.remove(entry.getKey());
return value;
}
}
return null;
}
/*public interface EventListener {
void onEvent(EventBase eventBase);
}*/
}
訂閱者
@Event(EventNotify.MAIN_INIT)
public void onEvent(EventBase eventBase) {
LogUtils.e("event main "+eventBase.getTag());
if(EventNotify.MAIN_INIT_RE_LOAD.equals(eventBase.getFlag())){
clearRefreshUserInfo();
initLoad();
}
}
傳送訊息不變
註冊的時候傳入object就是當前註冊類例項 用類名作為鍵 Subscription作為值存入map集合,當然啦需要判斷鍵是否存在防止多次註冊,反註冊的時候根據鍵找到Subscription並判斷沒有取消訂閱的時候進行取消訂閱。
在call方法中主要是根據當前註冊類找到Event註解方法 並且判斷EventBase的tag值和Event value值是否相同,相同就呼叫onEvent方法,起到篩選作用,當然傳送的時候EventBaseTag為空的時候或者Event value為空就不篩選,直接回調,EventBase的flag引數是給訂閱者onEvent方法接收到訊息進一步篩選型別的。
好啦,這是我在activity1註冊 並且onEvent的Event("mian"),activity2註冊 並onEvent的Event("mian2")
這是我在activity3傳送訊息 RxBus.getInstance().send(new EventBase("main","test","data"));
毫無疑問,activity1才能收到訊息。
別忘了在onDestory方法反註冊哦;
二、總結
僅供參考,有什麼地方寫的不好的 ,或者有錯誤,歡迎提出。