在Android的實際開發(fā)中赃绊,消息的通信是非常頻繁的既峡。結合Android的基礎知識,常用的通信方式有Intent碧查、Handler运敢、Broadcast,它們都可以在Activity忠售、Fragment传惠、Service之間相互通信,但是呢稻扬?從架構的思維考慮卦方,Android本身的通信方式會增加代碼架構的復雜度。為了解決這個問題泰佳,必須要重新定制一個新的通信方案盼砍。
通信方式有兩種:線程間通信
和跨進程通信
本文主要講解EventBus
框架的使用,它只能實現線程間的通信逝她,不支持跨進程通信衬廷。
[github地址]
首先,EventBus對應的github地址需要記住汽绢,如下:
https://github.com/greenrobot/EventBus
EventBus的版本會被更新的吗跋,所以,想要知道EventBus的最新版本,需要打開對應的github地址查看跌宛。
[簡單演示]
首先貼一下演示代碼:
public class MainActivity extends AppCompatActivity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注冊
EventBus.getDefault().register(this);
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
//發(fā)送消息
EventBus.getDefault().post(new MessageEvent(100));
}
}).start();
}
});
}
/**
* 接收消息
* @param event
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
/* Do something */
Log.d("yunchong", "MessageEvent的flag值為:"+event.getFlag());
}
@Override
protected void onDestroy() {
super.onDestroy();
//反注冊
EventBus.getDefault().unregister(this);
}
}
EventBus的使用非常簡單酗宋,如上代碼所示叁熔,在onCreate
方法里面注冊悼尾,在onDestroy
方法里面解注冊植康,代碼EventBus.getDefault().post
發(fā)送消息蹭越,以及onMessageEvent
方法接收消息射赛。這種寫法類似于Android的BroadCast
鲜戒,但和Android的BroadCast
有本質的區(qū)別境钟。
在一些項目中植酥,我經呈浚看到Activity和Fragment之間的通信使用廣播來實現翔烁,我對廣播的理解分為以下幾點:
- 常用的廣播分為Broadcast(常規(guī)廣播)和LocalBroadcast(本地廣播)。前者發(fā)送的消息旨涝,整個系統都能收到蹬屹,后者發(fā)送的消息只能當前應用可以收到。但是白华,由于大型項目中可能存在無數消息的傳遞慨默,這樣會導致廣播的泛濫,可讀性變差弧腥,如果非要使用廣播的話建議使用后者厦取;
- 廣播的注冊方式有兩種:動態(tài)注冊和靜態(tài)注冊,本人不推薦靜態(tài)注冊方式管搪,因為靜態(tài)注冊方式使廣播常駐內存虾攻,廣播是非常消耗內存的,靜態(tài)注冊方式不可扰自椤台谢;
- Android的廣播機制不僅可以跨線程也能跨進程,但是廣播的代碼實在不能用
優(yōu)雅
這個詞來形容岁经,從后期項目的維護成本出發(fā)朋沮,廣播的代碼還是比較紊亂的,加大了項目的維護成本缀壤。
所以樊拓,EventBus完全可以替代Android的廣播,不僅如此塘慕,任何跨線程
的通信都可以使用EventBus筋夏。那么,跨進程怎么玩图呢?Android的跨進程常常使用AIDL機制条篷,但考慮到代碼的簡潔性骗随,推薦使用Hermes
框架,Hermes
框架不是本章的重點赴叹,所以把思路重新回到EventBus上鸿染。
要使用EventBus,一般需要注冊乞巧,register方法的參數傳遞一個對象涨椒,大部分情況下,這個對象是當前Activity對象绽媒。
//注冊
EventBus.getDefault().register(this);
為了防止內存泄漏蚕冬,對應的還有反注冊,代碼如下:
EventBus.getDefault().unregister(this);
發(fā)送消息(發(fā)布者Publisher)是辕,post方法后面的參數可以傳遞基本數據類型或者對象囤热,建議傳遞一個對象。
EventBus.getDefault().post(new MessageEvent(100));
接收消息(發(fā)布者Subscriber)免糕,onMessageEvent
是接收消息的方法赢乓,這個方法必須添加注解@Subscribe
忧侧,不然發(fā)送消息時找不到內存中注解對應的onMessageEvent
方法石窑,其次onMessageEvent
方法必須有且只有一個形式參數,這個參數表示要接收的消息類型蚓炬,如果發(fā)送的消息對象非MessageEvent
對象松逊,那么onMessageEvent
方法是無法被執(zhí)行的。
/**
* 接收消息
* @param event
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
/* Do something */
Log.d("yunchong", "MessageEvent的flag值為:"+event.getFlag());
}
[線程模式]
接收消息的方法被@Subscribe
修飾肯夏,它是EventBus框架的自定義注解经宏,源碼如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
/**
* If true, delivers the most recent sticky event (posted with
* {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
*/
boolean sticky() default false;
/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s! */
int priority() default 0;
}
從源碼中得到的信息可以看出,該注解只能作用于方法上驯击,并且該方法在運行時被常駐到內存中烁兰。
它有三個成員:threadMode、sticky徊都、priority
priority:消息接收的優(yōu)先級沪斟,默認為0,優(yōu)先級越高就越快收到消息暇矫;
sticky:粘性事件的開關主之,默認值是false
threadMode:線程模式,默認模式是ThreadMode.POSTING李根,線程模式分為:POSTING
槽奕、MAIN
、MAIN_ORDERED
房轿、BACKGROUND
粤攒、ASYNC
POSTING:
默認的模式,開銷最小的模式所森,因為聲明為POSTING的訂閱者會在發(fā)布的同一個線程調用,發(fā)布者在主線程那么訂閱者也就在主線程夯接,反之亦必峰,避免了線程切換
,如果不確定是否有耗時操作钻蹬,謹慎使用吼蚁,因為可能是在主線程發(fā)布。MAIN
主線程調用问欠,視發(fā)布線程不同處理不同肝匆,如果發(fā)布者在主線程那么直接調用(非阻塞式),如果發(fā)布者不在主線程那么阻塞式調用顺献。MAIN_ORDERED
和MAIN差不多旗国,主線程調用,和MAIN不同的是他保證了post是非阻塞式的(默認走MAIN的非主線程的邏輯注整,所以可以做到非阻塞)BACKGROUND
在子線程調用能曾,如果發(fā)布在子線程那么直接在發(fā)布線程調用,如果發(fā)布在主線程那么將開啟一個子線程來調用肿轨,這個子線程是阻塞式的,按順序交付所有事件寿冕,所以也不適合做耗時任務,因為多個事件共用這一個后臺線程椒袍。ASYNC
在子線程調用驼唱,總是開啟一個新的線程來調用,適用于做耗時任務驹暑,比如數據庫操作玫恳,網絡請求等,不適合做計算任務优俘,會導致開啟大量線程京办。
[粘性事件]
EventBus的一般用法是:先注冊,再定義接收事件的方法帆焕,之后發(fā)送的事件才能接收到惭婿。
但是,在Android中有寫場景不符合EventBus的一般用法视搏,比如:Fragment之間切換時审孽,將數據從一個Fragment傳遞到另一個Fragment。還有一個場景是浑娜,從一個Activity跳轉到另一個Activity佑力,將數據從一個Activity傳遞到另一個Activity。
為了解決這個問題筋遭,EventBus推出了粘性事件
打颤。假如ActivityA跳轉到ActivityB時暴拄,將數據從A發(fā)送到B,那么該如何實現呢编饺?
第一步乖篷,在ActivityA中發(fā)送粘性事件
//發(fā)送消息
EventBus.getDefault().postSticky(new MessageEvent(100));
postSticky方法發(fā)送一個粘性事件。
第二步透且,在ActivityB中注冊和反注冊
//注冊
EventBus.getDefault().register(this);
//反注冊
EventBus.getDefault().unregister(this);
第三步撕蔼,在ActivityB中定義接收事件的方法
/**
* 接收消息
* @param event
*/
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onMessageEvent(MessageEvent event) {
/* Do something */
Log.d("yunchong", "MessageEvent的flag值為:"+event.getFlag());
}
其中,sticky屬性必須設置為true秽誊。
[代碼混淆]
根據EventBus作者的意思鲸沮,如果項目開啟混淆功能的話,還需要添加以下混淆代碼:
-keepattributes *Annotation*
-keepclassmembers class * {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# And if you use AsyncExecutor:
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
[本章完...]