實際項目開發過程中,經常遇到如下場景:不同的應用程序組件的控件間具有一定的相互關聯性,其中用戶對後者進行的某種操作會引起前者的相應改變。舉一個具體的場景:以新浪微博爲例,在新浪微博首頁好友動態列表頁和好友動態詳情頁(微博正文),對於每個詳情頁而言,佈局基本一致,在詳情頁點擊了個贊,讚的數量增加,同時讚的圖標發生了變化,此時返回到列表頁,此新浪微博首頁上的贊圖標以及數量與剛剛詳情頁的需要保持一致。再舉一個例子,對於多個底部導航tab下的資訊類閱讀app,在諮詢詳情頁點擊了收藏,然後收藏成功,此時回到底部tab中的個人中心,假如個人中心中有我的收藏,同時後面顯示的是收藏數量,此時此收藏數量需要同於於剛剛用戶所進行的收藏/取消收藏而即時更改數字。顯而易見,類似場景需求非常常見。
有時候,當此類需求相對簡單時,通過接口以實現回調等方式可以完成,但是當不同組件/控件之間的關係紛繁複雜時,基於接口的方案不僅使得代碼非常繁瑣,同時是的程序邏輯很混亂,基於此,Android事件總線框架(AndroidEventBus 、EventBus、Otto) ,爲此類需求的實現提供了非常方便的方案。
這裏我以AndroidEventBus框架爲例,爲大家實現該功能。
AndroidEventBus框架 基本結構
你可以按照下面幾個步驟來使用AndroidEventBus.
1.
註冊事件接收對象
public class YourActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
// 註冊對象
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
// 註銷
EventBus.getDefault().unregister(this);
super.onDestroy();
}
}
2. 通過Subscriber註解來標識事件接收對象中的接收方法
public class YourActivity extends Activity {
// code ......
// 接收方法,默認的tag,執行在UI線程
@Subscriber
private void updateUser(User user) {
Log.e("", "### update user name = " + user.name);
}
// 含有my_tag,當用戶post事件時,只有指定了"my_tag"的事件纔會觸發該函數,執行在UI線程
@Subscriber(tag = "my_tag")
private void updateUserWithTag(User user) {
Log.e("", "### update user with my_tag, name = " + user.name);
}
// 含有my_tag,當用戶post事件時,只有指定了"my_tag"的事件纔會觸發該函數,
// post函數在哪個線程執行,該函數就執行在哪個線程
@Subscriber(tag = "my_tag", mode=ThreadMode.POST)
private void updateUserWithMode(User user) {
Log.e("", "### update user with my_tag, name = " + user.name);
}
// 含有my_tag,當用戶post事件時,只有指定了"my_tag"的事件纔會觸發該函數,執行在一個獨立的線程
@Subscriber(tag = "my_tag", mode = ThreadMode.ASYNC)
private void updateUserAsync(User user) {
Log.e("", "### update user async , name = " + user.name + ", thread name = " + Thread.currentThread().getName());
}
}
User類大致如下 : public class User {
String name ;
public User(String aName) {
name = aName ;
}
}
接收函數使用tag來標識可接收的事件類型,與BroadcastReceiver中指定action是一樣的,這樣可以精準的投遞消息。mode可以指定目標函數執行在哪個線程,默認會執行在UI線程,方便用戶更新UI。目標方法執行耗時操作時,可以設置mode爲ASYNC,使之執行在子線程中。
3.
在其他組件,例如Activity, Fragment,Service中發佈事件
EventBus.getDefault().post(new User("android"));
// post a event with tag, the tag is like broadcast's action
EventBus.getDefault().post(new User("mr.simple"), "my_tag");
發佈事件之後,註冊了該事件類型的對象就會接收到響應的事件.
Android Studio集成
<span style="font-weight: normal;"><span style="font-size:18px;">dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile files('libs/androideventbus-1.0.5.jar')
}
</span></span>
我的simple例子
MainActivity
package com.cloudhome.androideventbus;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import org.simple.eventbus.EventBus;
import org.simple.eventbus.Subscriber;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
//eventBus 註冊
EventBus.getDefault().register(this);
}
//當在OtherActivity點擊了更新按鈕時,該方法會被執行,
//接受從OtherActivity傳過來的參數,並顯示在界面上
@Subscriber(tag = "update")
public void update(String msg){
TextView tv = (TextView) findViewById(R.id.tv);
tv.setText(msg);
}
//點擊按鈕跳轉到OtherActivity
public void toOtherActivity(View view){
startActivity(new Intent(this, OtherActivity.class));
}
//點擊按鈕跳轉到TestProgressBarActivity
public void updateProgressbar(View view){
startActivity(new Intent(this, TestProgressBarActivity.class));
}
// EventBus不要忘記註銷!!!!
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
OtherActivity.java
package com.cloudhome.androideventbus;
import org.simple.eventbus.EventBus;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
public class OtherActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_other);
EventBus.getDefault().register(this);
}
//更新MainActivity
public void updateClick(View view){
String msg = "this msg was send by OtherActivity";
//這裏post了一個帶tag的消息,將msg傳給MainActivity,MainActivity中帶有名爲update的tag的方法會被執行
EventBus.getDefault().post(msg, "update");
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
TestProgressBarActivity.java
package com.cloudhome.androideventbus;
import org.simple.eventbus.EventBus;
import org.simple.eventbus.Subscriber;
import org.simple.eventbus.ThreadMode;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ProgressBar;
public class TestProgressBarActivity extends Activity {
private ProgressBar pb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_testprogressbar);
pb = (ProgressBar) findViewById(R.id.pb);
EventBus.getDefault().register(this);
}
//模擬下載
public void btnClick(View view){
//這裏post了一個帶tag的消息,則下面對應該tag的方法會被執行
EventBus.getDefault().post(view, "start download");
}
/**
* 這裏的mode屬性指定了該方法執行在哪個線程,有三個取值:MAIN, POST, ASYNC
* MAIN即該方法執行在UI線程,ASYNC即該方法執行在異步線程,POST則代表該方法
* 執行在與被調用的post方法相同的線程中,這裏因爲是模擬耗時的網絡操作,所以
* 該方法一定要異步執行,若將mode改爲MAIN,則界面會阻塞
* @param view
*/
@Subscriber(tag = "start download", mode = ThreadMode.ASYNC)
public void startDownload(View view){
int progress = 0;
while(progress <= 100){
progress += 10;
//這裏post當前的下載進度,下面與這裏指定的tag相同的方法會被執行
EventBus.getDefault().post(progress, "update progress");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//該方法的參數就是上面post的數據,在這裏更新UI,且在主線程執行該方法
@Subscriber(tag = "update progress", mode = ThreadMode.MAIN)
public void updateProgress(int progress){
pb.setProgress(progress);
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}