轉(zhuǎn)載文章請(qǐng)標(biāo)明出處http://www.reibang.com/p/476a3a6dc789
其他同系列文章
當(dāng)初學(xué)Android的時(shí)候,無(wú)意間發(fā)現(xiàn)了EventBus這個(gè)庫(kù)寞冯,就像發(fā)現(xiàn)了一片新大陸一樣。居然可以直接在Android的組件之間互相傳遞數(shù)據(jù),為啥作者就這么牛逼呢?為啥能弄出這么叼的東西呻征?隨著知識(shí)的不斷累計(jì),發(fā)現(xiàn)其實(shí)當(dāng)初覺得非常神秘,遙不可及的東西悠菜,其實(shí)原理相當(dāng)?shù)暮?jiǎn)單。對(duì)败富,簡(jiǎn)單到你也可以寫出來(lái)的悔醋。下面,我就來(lái)教教大家如何從零開始“擼”出自己的EventBus兽叮。當(dāng)然篙顺,你得具備一定得Java基礎(chǔ)以及Android基礎(chǔ),不然你也不會(huì)看到這篇文章~
原理
先來(lái)講講原理啊~網(wǎng)上得說(shuō)法幾乎都是講EventBus是基于觀察者模式的充择。嗯,那么究竟誰(shuí)是觀察者匪蟀,誰(shuí)是被觀察者呢椎麦?EventBus這個(gè)東東本身是觀察者還是被觀察者?按照我的理解材彪,EventBus它既不是觀察者观挎,也不是被觀察者,它不過(guò)是連接觀察者和被觀察者之間的橋梁段化。那么觀察者是誰(shuí)嘁捷?觀察者就是在你調(diào)用EventBus.getDefault().register(this)中的'this',它可能是個(gè)Activity、Fragment显熏,也可能是個(gè)Service啥的(其實(shí)更準(zhǔn)確的說(shuō)應(yīng)該是其中你onEvent開頭或標(biāo)記了Subscribe注解的方法)雄嚣。那么被觀察者自然就是所有可以向EventBus傳遞消息對(duì)象。這里盜用一張EventBus官網(wǎng)的圖。
夢(mèng)想起航
好的缓升,了解了原理我們現(xiàn)在開始來(lái)“擼”代碼鼓鲁。從第一步開始,首先建個(gè)工程骇吭,起名就叫MiniBus吧,畢竟我們沒(méi)有人家強(qiáng)大燥狰。等我們強(qiáng)大起來(lái)就改名叫SuperBus斜筐。哈哈哈哈哈哈龙致。。奴艾。。<br />首先新建個(gè)類就叫MiniBus,和工程名相同像啼。首先明確其需要基于單例模式,畢竟不希望使用者可以new出來(lái)好幾個(gè)MiniBus潭苞,那樣的話這些觀察者就沒(méi)法管理了忽冻。單例這種東西對(duì)于身經(jīng)百戰(zhàn)的我們來(lái)說(shuō)是So easy。在這里此疹,我就隨意選了一種單例的寫法。
public class MiniBus {
private MiniBus() {
}
private static class BusHolder {
private static MiniBus instance = new MiniBus();
}
public static MiniBus getInstance() {
return BusHolder.instance;
}
}
然后呢湖笨?我們需要暴露出我們的方法蹦骑。包括觀察者注冊(cè)的方法register()、觀察者取消注冊(cè)的方法unregister()眠菇、被觀察者發(fā)送事件的方法post()。嗯笑窜,暫時(shí)就這三個(gè)方法吧登疗。
//觀察者注冊(cè)
public void register(Object subscriber){}
//觀察者取消注冊(cè)
public void unregister(Object subscriber){}
//被觀察者發(fā)送消息,在此暫時(shí)不考慮粘性事件等復(fù)雜情況
public void post(Object event){}
至此匾寝,MiniBus大致的骨架已經(jīng)搭建好了,接下來(lái)就要依照這些骨架填充好其血肉啦(為啥感覺這句話這么血腥艳悔?)。<br />
沖浪~
首先來(lái)看看register()方法該做一些什么工作抡锈。
初級(jí)方案
最剛開始我想的就是直接把注冊(cè)的觀察者直接存到一個(gè)List集合里面乔外,然后當(dāng)有post請(qǐng)求事件發(fā)出的時(shí)候遍歷這些觀察者拿到對(duì)應(yīng)的該執(zhí)行的方法,然后通過(guò)反射拿到這些方法去執(zhí)行撇簿。當(dāng)然,這種方法是可行的四瘫,但是存在一個(gè)問(wèn)題欲逃。那就是每次有事件發(fā)出都要去遍歷一遍觀察者的所有方法,效率很低洗做。
好一點(diǎn)的方案
改進(jìn)的方案就是以犧牲空間換取時(shí)間的方法彰居。在注冊(cè)的時(shí)候直接遍歷出該對(duì)象的有效方法,然后存儲(chǔ)為和subscriber對(duì)應(yīng)的Map類型的數(shù)據(jù),這樣的話陈惰,每次post事件發(fā)出是僅僅只需要遍歷有效的方法,效率上要比初級(jí)方案遍歷每次都遍歷所有的方法要高很多。
更好的方案
第二種方案雖然提升了不小的效率画髓,但是仍然會(huì)在每次請(qǐng)求的時(shí)候遍歷所有的有效方法平委。通過(guò)仔細(xì)思考,在第二種方案上做一絲絲改動(dòng),就是不以subscriber為Map的key值匾鸥,而是以event的類型作為Map的key值碉纳,這樣的話就不用每次post請(qǐng)求可以直接通過(guò)key值取出對(duì)應(yīng)的方法,都遍歷一遍那么多的方法啦~<br />
雖然第三種方案相對(duì)最好奴愉,但是這里我為了簡(jiǎn)單起見铁孵,選擇了折中的第二種方案。下面我們就來(lái)徒手?jǐn)]代碼蜕劝。
public class MiniBus {
private MiniBus() {
}
private static class BusHolder {
private static MiniBus instance = new MiniBus();
//存儲(chǔ)觀察者以及其有效的觀察者方法
private static Map<Object, List<Method>> subMethods = new HashMap<>();
}
public static MiniBus getInstance() {
return BusHolder.instance;
}
public void register(Object subscriber) {
Class<?> subClass = subscriber.getClass();
Method[] methods = subClass.getDeclaredMethods();
List<Method> usefulMethods = new ArrayList<>();
//遍歷觀察者所有的方法岖沛,拿到所有有效的方法
for (Method method : methods) {
if (checkIsUsefulMethod(method)) {
usefulMethods.add(method);
}
}
if (usefulMethods.size() > 0) {
BusHolder.subMethods.put(subscriber, usefulMethods);
}
}
/**
* 判斷一個(gè)方法是否是有效的觀察者方法,
* 在此先采用EventBus3.0版本以前的方式烫止,
* 也就是以onEvent開頭的方法馆蠕,返回值為void
* 并且只有一個(gè)參數(shù),即視為有效方法
*/
private boolean checkIsUsefulMethod(Method method) {
String methodName = method.getName();
Class<?> returnType = method.getReturnType();
Class<?>[] paramsClass = method.getParameterTypes();
if (methodName.startsWith("onEvent")
&& returnType.equals(Void.TYPE)
&& paramsClass.length == 1) {
return true;
}
return false;
}
}
接下來(lái)看看unregister()方法的實(shí)現(xiàn)互躬。那就簡(jiǎn)單了吼渡,直接移除該key值以及對(duì)應(yīng)的數(shù)據(jù)就好了。
public void unregister(Object subscriber) {
BusHolder.subMethods.remove(subscriber);
}
post()方法的實(shí)現(xiàn)剛才已經(jīng)講過(guò)了寺酪,就是遍歷所有的觀察者方法,然后比對(duì)事件類型得滤,若事件類型相同即為對(duì)應(yīng)的觀察者方法盒犹,即對(duì)其發(fā)送post事件消息眨业。
public void post(final Object event) {
Class<?> eventClass = event.getClass();
Set<Object> keys = BusHolder.subMethods.keySet();
for (final Object key : keys) {
List<Method> methods = BusHolder.subMethods.get(key);
for (final Method method : methods) {
if (method.getParameterTypes()[0].equals(eventClass)) {
//打開對(duì)私有方法的調(diào)用權(quán)限
method.setAccessible(true);
try {
method.invoke(key, event);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
至此沮协,一個(gè)簡(jiǎn)單的EventBus原型已完成慷暂。雖然它還存在不少問(wèn)題,但是EventBus最核心的功能已經(jīng)實(shí)現(xiàn)呜呐。讓我們建兩個(gè)Activity試試我們自己徒手?jǐn)]出來(lái)的EventBus。<br />
著陸
第一個(gè)Activity測(cè)試代碼洋机。
public class MainActivity extends AppCompatActivity {
private boolean first = true;
private int count = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MiniBus.getInstance().register(this);
}
@Override
protected void onResume() {
super.onResume();
if(first){
startActivity(new Intent(this, SecondActivity.class));
first = false;
}
}
//觀察方法
private void onEventMain(String str) {
Toast.makeText(getApplicationContext(), str, Toast.LENGTH_SHORT).show();
((TextView) findViewById(R.id.tv))
.setText("你點(diǎn)了" + (++count) + "下");
}
}
第二個(gè)Activity測(cè)試代碼洋魂。
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(){
@Override
public void run() {
MiniBus.getInstance().post("我?guī)?);
}
}.start();
}
});
}
}
在第一個(gè)Activity里面定義了一個(gè)OnEventMain()的觀察方法副砍,向MiniBus注冊(cè)了觀察者,并在onResume()的時(shí)候直接跳轉(zhuǎn)到第二個(gè)Activity豁翎。第二個(gè)Activity設(shè)置了一個(gè)點(diǎn)擊事件,點(diǎn)擊的時(shí)候通知第一個(gè)Activity邦尊。熟悉EventBus的朋友應(yīng)該很熟悉优烧,就和EventBus2.x版本的用法幾乎一樣。然后運(yùn)行項(xiàng)目畦娄,發(fā)現(xiàn)點(diǎn)擊按鈕第一個(gè)Activity能成功接收并處理消息熙卡。Oh,yes!