首先放一張官方推薦的app設(shè)計架構(gòu)圖,想要了解更多(芝麻之門)
LocalBroadcastManager
冷落的LBM
說LocalBroadcastManager有點冷落,一個是很少人知道并且合理使用廣播,很多人要么使用的是系統(tǒng)的全局廣播BraoadCastRecever,要么使用EventBus或RxAndroid等等其他觀察者模式的三方庫,慢慢的就失寵了。然鵝,并不只是這樣,當現(xiàn)在你的項目遷移到Androidx時,會發(fā)現(xiàn)官方居然把它廢棄了,隨后官方明確指出最適合的替代品LiveData和reactive streams,見以下描述 (官方傳送門)
關(guān)于LBM
LocalBroadcastManager顧名思義就是本地廣播,也是基于觀察者模式的事件總線,用于應(yīng)用內(nèi)通信,比較安全和高效,雖然遷移到androidx后,在1.1.0高版本LocalBroadcastManager被標記過時,但是它的用處還是蠻大的,相比系統(tǒng)的廣播,本地廣播有著無法比擬的優(yōu)越性,而且非常高效,相比第三方觀察者模式庫,滿足條件下我寧愿使用1.0.0版本的LocalBroadcastManager。
與系統(tǒng)廣播的區(qū)別
- 范圍上:
LocalBroadcastManager為本地廣播,只能接受自身App發(fā)送的廣播,只能用于應(yīng)用內(nèi)之間的通信,范圍相對較??;而系統(tǒng)的BraoadCastRecever可以實現(xiàn)跨進程通訊,范圍更大。 - 效率上:
LocalBroadcastManager通信核心是Handler,所以只能用于應(yīng)用內(nèi)通信,安全和效率都很高;而系統(tǒng)的BraoadCastRecever通信核心是Binder機制,
實現(xiàn)跨進程通信,范圍更廣,導致運行效率稍微遜一點。 - 安全上:
LocalBroadcastManager由于核心是Handler,而且只能動態(tài)注冊,只能用于app內(nèi)通信,安全上更加的有保障;而系統(tǒng)的BraoadCastRecever容易被利用,安全上相對較弱一點。
使用方式
注冊廣播:
IntentFilter filter = new IntentFilter();
filter.addAction(“你的Action”);
LocalBroadcastManager.getInstance(Context context).registerReceiver(BroadcastReceiver receiver, filter);
發(fā)送廣播:
LocalBroadcastManager.getInstance(Context context).sendBroadcast(Intent intent);
取消注冊:
LocalBroadcastManager.getInstance(this).unregisterReceiver(BroadcastReceiver receiver);
接收廣播:
final class MyBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
if(intent!=null && “你的Action”.equals(intent.getAction())){
// 處理邏輯
}
}
}
在《阿里巴巴Android開發(fā)手冊(正式版)_v1.0.0》中有明確指出關(guān)于廣播的使用規(guī)范問題,以下規(guī)范等級為強制,所以我們應(yīng)用合理正確的使用廣播,遵循綠色公約,避免信息泄露和攔截意圖的風險,以下為規(guī)范描述:
如果廣播僅限于應(yīng)用內(nèi),則可以使用
LocalBroadcastManager#sendBroadcast()實現(xiàn),避免敏感信息外泄和 Intent 攔截的風險。
源碼分析
@NonNull
public static LocalBroadcastManager getInstance(@NonNull Context context) {
synchronized (mLock) {
if (mInstance == null) {
mInstance = new LocalBroadcastManager(context.getApplicationContext());
}
return mInstance;
}
}
private LocalBroadcastManager(Context context) {
mAppContext = context;
mHandler = new Handler(context.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_EXEC_PENDING_BROADCASTS:
executePendingBroadcasts();
break;
default:
super.handleMessage(msg);
}
}
};
}
初始化上LocalBroadcastManager明顯采取的是懶漢模式的單例模式,并且初始化的時候創(chuàng)建了一個Handler對象,在主線程上進行工作,用于處理發(fā)送的廣播,這就是和系統(tǒng)BroadCastReciver的最大區(qū)別,系統(tǒng)廣播則是通過Binder機制進行通信的,而本地廣播采取的是Handler機制進行通信。
private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers = new HashMap<>();
private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();
private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();
public void registerReceiver(@NonNull BroadcastReceiver receiver,
@NonNull IntentFilter filter) {
synchronized (mReceivers) {
ReceiverRecord entry = new ReceiverRecord(filter, receiver);
ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
if (filters == null) {
filters = new ArrayList<>(1);
mReceivers.put(receiver, filters);
}
filters.add(entry);
for (int i=0; i<filter.countActions(); i++) {
String action = filter.getAction(i);
ArrayList<ReceiverRecord> entries = mActions.get(action);
if (entries == null) {
entries = new ArrayList<ReceiverRecord>(1);
mActions.put(action, entries);
}
entries.add(entry);
}
}
}
注冊廣播則明顯能夠看出是通過2個Map嵌套集合mReceivers和mActions來進行協(xié)調(diào)運作的,而mReceivers則是以receiver作為key,存儲的是一個接收記錄ReceiverRecord集合,而ReceiverRecord是用來存儲篩選器IntentFilter與廣播接收器BroadcastReceiver及廣播運行狀態(tài)的,但是由于LocalBroadcastManager是單例模式,可以存放多個廣播接收器BroadcastReceiver故采用的集合嵌套,同理mActions是以篩選器中的action作為key,存儲的也是ReceiverRecord集合。
public boolean sendBroadcast(@NonNull Intent intent) {
synchronized (mReceivers) {
final String action = intent.getAction();
final String type = intent.resolveTypeIfNeeded(
mAppContext.getContentResolver());
final Uri data = intent.getData();
final String scheme = intent.getScheme();
final Set<String> categories = intent.getCategories();
==========================省略部分===================
if (receivers != null) {
for (int i=0; i<receivers.size(); i++) {
receivers.get(i).broadcasting = false;
}
mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
}
return true;
}
}
}
return false;
}
明顯看出本地廣播是通過Handler進行發(fā)送消息的,檢查排隊隊列是否有未發(fā)布的MSG_EXEC_PENDING_BROADCASTS,沒有的話發(fā)送消息。
void executePendingBroadcasts() {
while (true) {
final BroadcastRecord[] brs;
synchronized (mReceivers) {
final int N = mPendingBroadcasts.size();
if (N <= 0) {
return;
}
brs = new BroadcastRecord[N];
mPendingBroadcasts.toArray(brs);
mPendingBroadcasts.clear();
}
for (int i=0; i<brs.length; i++) {
final BroadcastRecord br = brs[i];
final int nbr = br.receivers.size();
for (int j=0; j<nbr; j++) {
final ReceiverRecord rec = br.receivers.get(j);
if (!rec.dead) {
rec.receiver.onReceive(mAppContext, br.intent);
}
}
}
}
}
當工作線程接收到Handler發(fā)送的消息時候,會執(zhí)行executePendingBroadcasts()方法,發(fā)送廣播的時候mPendingBroadcasts來存儲Intent意圖與廣播接收器ReceiverRecord,當處理廣播的時候,從mPendingBroadcasts中取出信息存放到廣播接收器數(shù)組BroadcastRecord[]中,通過循環(huán)調(diào)用廣播接收的抽象方法onReceive(Context context,Intent intent)進行消息發(fā)送到前臺主線程,就這樣完成了應(yīng)用內(nèi)通信。
初識LiveData
以下翻譯可能有槽點,去官網(wǎng)查看更多更詳細的信息(芝麻之門)
介紹
LiveData是官方架構(gòu)組件,是Jetpack眾多組件的一個,它是一個可觀察的數(shù)據(jù)持有者類。與常規(guī)的可觀察對象不同,LiveData是生命周期感知的,這意味著它尊重其他應(yīng)用程序組件的生命周期,比如activities、fragments或者services。這種意識確保LiveData只更新處于活動生命周期狀態(tài)的應(yīng)用程序組件觀察者。
LiveData認為,如果一個觀察者的生命周期處于STARTED或RESUMED狀態(tài),那么這個觀察者(由observer類表示)就處于活動狀態(tài)。LiveData只將更新通知活動觀察者。注冊為監(jiān)視LiveData對象的非活動觀察者不會收到有關(guān)更改的通知。
您可以注冊一個與實現(xiàn)LifecycleOwner接口的對象配對的觀察者。當相應(yīng)的生命周期對象的狀態(tài)更改為DESTROYED時,此關(guān)系允許刪除觀察者。這對于活動和片段特別有用,因為它們可以安全地觀察LiveData對象,而不用擔心泄漏——當activities和fragments的生命周期被破壞時,它們會立即取消訂閱。
優(yōu)勢
UI界面與數(shù)據(jù)實時保證一致
LiveData遵循觀察者模式。當生命周期狀態(tài)發(fā)生變化時,LiveData通知觀察者Observer對象。您可以合并代碼來更新這些觀察者對象中的UI。您的觀察者可以在每次發(fā)生更改時更新UI,而不是每次應(yīng)用程序數(shù)據(jù)更改時都更新UI不會造成內(nèi)存泄漏
觀察者被綁定到生命周期Lifecycle對象,并在其關(guān)聯(lián)的生命周期被destroyed后進行清理。當界面銷毀或者停止活動不會造成崩潰
如果觀察者的生命周期是不活動的,例如在后堆棧中的活動,那么它不會接收任何LiveData事件。不需要手動處理生命周期
UI組件只觀察相關(guān)數(shù)據(jù),不停止或恢復觀察。LiveData自動管理所有這些,因為它在觀察過程中知道相關(guān)的生命周期狀態(tài)變化。總是保證最新的數(shù)據(jù)
如果一個生命周期變?yōu)椴换顒拥?,它將在再次活動時接收最新的數(shù)據(jù)。例如,在后臺的活動在返回到前臺后立即接收最新的數(shù)據(jù)。適當?shù)呐渲酶?br> 如果某個
activitys或fragments由于配置更改(如設(shè)備旋轉(zhuǎn))而重新創(chuàng)建,它將立即接收最新可用數(shù)據(jù)。共享資源和數(shù)據(jù)
您可以使用singleton模式擴展LiveData對象來包裝系統(tǒng)服務(wù),以便在您的應(yīng)用程序中共享它們。LiveData對象連接到系統(tǒng)服務(wù)一次,然后任何需要該資源的觀察者都可以查看LiveData對象。有關(guān)更多信息,請參見Extend LiveData。
屬性及方法
| 返回類型 | 方法 | 說明 |
|---|---|---|
| T | getValue() | 返回T類型的對象值 |
| boolean | hasActiveObservers() | 當前是否有活動的觀察者observers |
| boolean | hasObservers() | 當前是否有觀察者observers |
| void | observe(LifecycleOwner owner, Observer<? super T> observer) | 添加觀察者對象到列表,配合生命周期 |
| void | observeForever(Observer<? super T> observer) | 添加觀察者對象到列表,無生命周期 |
| void | removeObserver(Observer<? super T> observer) | 從觀察者列表中移除給定的觀察者 |
| void | removeObservers(LifecycleOwner owner) | 刪除與給定生命周期所有者綁定的所有觀察者 |
| void | onActive() | 當活動觀察者的數(shù)量從0變?yōu)?時調(diào)用 |
| void | onInactive() | 當活動觀察者的數(shù)量從1變?yōu)?時調(diào)用 |
| void | postValue(T value) | 將任務(wù)發(fā)布到主線程以設(shè)置給定值 |
| void | setValue(T value) | 設(shè)置更新數(shù)據(jù)源 |
使用步驟
- 依賴
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:1.1.1"
// just ViewModel
implementation "android.arch.lifecycle:viewmodel:1.1.1"
// just LiveData
implementation "android.arch.lifecycle:livedata:1.1.1"
- 創(chuàng)建LiveData實例來保存特定類型的數(shù)據(jù)。通常會結(jié)合
ViewModel一起使用,也可以單獨使用。
public class NameViewModel extends ViewModel {
// Create a LiveData with a String
private MutableLiveData<String> currentName;
public MutableLiveData<String> getCurrentName() {
if (currentName == null) {
currentName = new MutableLiveData<String>();
}
return currentName;
}
// Rest of the ViewModel...
}
注意:確保將更新UI的LiveData對象存儲在ViewModel對象中,而不是存儲在Activity或Fragment中,原因如下:
以避免膨脹的Activity和Fragment?,F(xiàn)在這些UI控制器負責顯示數(shù)據(jù),而不是保存數(shù)據(jù)狀態(tài)。
將LiveData實例與特定的Activity或Fragment實例解耦,并允許LiveData對象在配置更改后仍然存在。
- 創(chuàng)建一個觀察者對象
Observer,該對象定義onChanged()方法,該方法控制LiveData對象持有的數(shù)據(jù)更改時發(fā)生的情況。通常在UI控制器中創(chuàng)建一個觀察者對象,例如一個Activity或者fragment。
public class NameActivity extends AppCompatActivity {
private NameViewModel model;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other code to setup the activity...
// Get the ViewModel.
model = ViewModelProviders.of(this).get(NameViewModel.class);
// Create the observer which updates the UI.
final Observer<String> nameObserver = new Observer<String>() {
@Override
public void onChanged(@Nullable final String newName) {
// Update the UI, in this case, a TextView.
nameTextView.setText(newName);
}
};
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
model.getCurrentName().observe(this, nameObserver);
}
}
在大多數(shù)情況下,應(yīng)用程序組件的onCreate()方法是開始觀察LiveData對象的合適位置,原因如下:
以確保系統(tǒng)不會從活動或fragment的onResume()方法發(fā)出冗余調(diào)用。
確?;顒踊蚱尉哂袛?shù)據(jù),可以在活動時立即顯示這些數(shù)據(jù)。一旦應(yīng)用程序組件處于啟動狀態(tài),它就會從它所觀察的LiveData對象接收到最近的值。只有在設(shè)置了要觀察的LiveData對象時才會發(fā)生這種情況。
通常,LiveData只在數(shù)據(jù)發(fā)生更改時提供更新,并且只向活動觀察者提供更新。這種行為的一個例外是,當觀察者從非活動狀態(tài)更改為活動狀態(tài)時,也會收到更新。此外,如果觀察者第二次從非活動狀態(tài)更改為活動狀態(tài),則只有當值自上次活動以來發(fā)生更改時,才會接收到更新。
- 使用observe()方法將觀察者對象附加到LiveData對象。方法接受
LifecycleOwner對象。這訂閱觀察者對象到LiveData對象,以便在發(fā)生更改時通知它。通常將觀察者對象附加到UI控制器中,例如Activity或Fragment
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
model.getCurrentName().observe(this, nameObserver);
您可以使用observeForever(observer)方法注冊一個沒有關(guān)聯(lián)LifecycleOwner對象的觀察者。在這種情況下,觀察者總是被認為是活躍的,因此總是被通知修改。您可以刪除調(diào)用removeObserver(Observer)方法的這些觀察者。
- LiveData擴展用法
public class StockLiveData extends LiveData<BigDecimal> {
private StockManager stockManager;
private SimplePriceListener listener = new SimplePriceListener() {
@Override
public void onPriceChanged(BigDecimal price) {
setValue(price);
}
};
public StockLiveData(String symbol) {
stockManager = new StockManager(symbol);
}
@Override
protected void onActive() {
stockManager.requestPriceUpdates(listener);
}
@Override
protected void onInactive() {
stockManager.removeUpdates(listener);
}
}
當LiveData對象具有活動觀察者時,將調(diào)用onActive()方法。這意味著您需要開始從該方法觀察股票價格更新。
當LiveData對象沒有任何活動的觀察者時,將調(diào)用onInactive()方法。因為沒有觀察者在聽,所以沒有理由保持與StockManager服務(wù)的連接。
setValue(T)方法更新LiveData實例的值,并將更改通知任何活動的觀察者。
public class MyFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
LiveData<BigDecimal> myPriceListener = ...;
myPriceListener.observe(this, price -> {
// Update the UI.
});
}
}
方法將fragment(它是LifecycleOwner的一個實例)作為第一個參數(shù)傳遞。這樣做意味著這個觀察者被綁定到與所有者關(guān)聯(lián)的Lifecycle對象上,這意味著:
如果Lifecycle對象沒有處于活動狀態(tài),那么即使值發(fā)生了更改,也不會調(diào)用觀察者
銷毀生命周期對象后,將自動刪除觀察者。
LiveData對象是生命周期感知的,這意味著您可以在多個Activitys、Fragments和service之間共享它們。為了保持示例的簡單性,可以將LiveData類實現(xiàn)為單例,如下所示:
public class StockLiveData extends LiveData<BigDecimal> {
private static StockLiveData sInstance;
private StockManager stockManager;
private SimplePriceListener listener = new SimplePriceListener() {
@Override
public void onPriceChanged(BigDecimal price) {
setValue(price);
}
};
@MainThread
public static StockLiveData get(String symbol) {
if (sInstance == null) {
sInstance = new StockLiveData(symbol);
}
return sInstance;
}
private StockLiveData(String symbol) {
stockManager = new StockManager(symbol);
}
@Override
protected void onActive() {
stockManager.requestPriceUpdates(listener);
}
@Override
protected void onInactive() {
stockManager.removeUpdates(listener);
}
}
在Activity、Fragment、Service中可以這么調(diào)用,通過單例實現(xiàn)共享數(shù)據(jù)
public class MyFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
StockLiveData.get(symbol).observe(this, price -> {
// Update the UI.
});
}
}
轉(zhuǎn)化 LiveData
在將LiveData對象發(fā)送給觀察者之前,您可能希望更改存儲在LiveData對象中的值,或者您可能需要根據(jù)另一個LiveData實例的值返回另一個LiveData實例。生命周期包提供轉(zhuǎn)換類,該類包含支持這些場景的helper方法。
Transformations.map ()
對存儲在LiveData對象中的值應(yīng)用函數(shù),并將結(jié)果向下傳播
LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
Transformations.switchMap()
與map()類似,將函數(shù)應(yīng)用于存儲在LiveData對象中的值,并將結(jié)果解包并向下分派。傳遞給switchMap()的函數(shù)必須返回LiveData對象,如下例所示:
private LiveData<User> getUser(String id) {
...;
}
LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );
您可以使用轉(zhuǎn)換方法在觀察者的生命周期中攜帶信息。除非觀察者正在觀察返回的LiveData對象,否則不會計算轉(zhuǎn)換。因為轉(zhuǎn)換是延遲計算的,所以與生命周期相關(guān)的行為是隱式傳遞的,不需要額外的顯式調(diào)用或依賴關(guān)系。
如果您認為在ViewModel對象中需要一個生命周期對象,那么轉(zhuǎn)換可能是一個更好的解決方案。例如,假設(shè)您有一個UI組件,它接受一個地址并返回該地址的郵政編碼。您可以為這個組件實現(xiàn)簡單的視圖模型,如下面的示例代碼所示:
class MyViewModel extends ViewModel {
private final PostalCodeRepository repository;
public MyViewModel(PostalCodeRepository repository) {
this.repository = repository;
}
private LiveData<String> getPostalCode(String address) {
// DON'T DO THIS,不推薦
return repository.getPostCode(address);
}
}
然后,UI組件需要從以前的LiveData對象注銷注冊,并在每次調(diào)用getPostalCode()時注冊到新實例。此外,如果重新創(chuàng)建UI組件,它將觸發(fā)對repository.getPostCode()方法的另一個調(diào)用,而不是使用前一個調(diào)用的結(jié)果。
相反,您可以將郵政編碼查詢實現(xiàn)為地址輸入的轉(zhuǎn)換,如下面的示例所示:
class MyViewModel extends ViewModel {
private final PostalCodeRepository repository;
private final MutableLiveData<String> addressInput = new MutableLiveData();
public final LiveData<String> postalCode =
Transformations.switchMap(addressInput, (address) -> {
return repository.getPostCode(address);
});
public MyViewModel(PostalCodeRepository repository) {
this.repository = repository
}
private void setInput(String address) {
addressInput.setValue(address);
}
}
在這種情況下,該postalCode字段被定義為轉(zhuǎn)換addressInput。只要您的應(yīng)用程序具有與該postalCode字段關(guān)聯(lián)的活動觀察者,就會在addressInput更改時重新計算并檢索字段的值 。
此機制允許較低級別的應(yīng)用程序創(chuàng)建LiveData按需延遲計算的對象。甲ViewModel對象可以容易地獲得以引用LiveData的對象,然后在它們的頂部限定的變換規(guī)則。
創(chuàng)建新的轉(zhuǎn)換
有十幾種不同的特定轉(zhuǎn)換可能對您的應(yīng)用有用,但默認情況下不提供。要實現(xiàn)自己的轉(zhuǎn)換,您可以使用MediatorLiveData該類,該類偵聽其他LiveData對象并處理它們發(fā)出的事件。MediatorLiveData正確地將其狀態(tài)傳播到源LiveData對象。要了解有關(guān)此模式的更多信息,請參閱Transformations 該類的參考文檔 。
合并多個LiveData源
MediatorLiveData是一個子類LiveData,允許您合并多個LiveData源。MediatorLiveData 只要任何原始LiveData源對象發(fā)生更改,就會觸發(fā)對象的觀察者。
例如,如果LiveDataUI中有一個可以從本地數(shù)據(jù)庫或網(wǎng)絡(luò)更新的對象,則可以將以下源添加到該 MediatorLiveData對象:
LiveData與存儲在數(shù)據(jù)庫中的數(shù)據(jù)關(guān)聯(lián)的對象。
LiveData與從網(wǎng)絡(luò)訪問的數(shù)據(jù)關(guān)聯(lián)的對象。
LiveData liveData1 = ...;
LiveData liveData2 = ...;
MediatorLiveData liveDataMerger = new MediatorLiveData<>();
liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
總結(jié)
讀到這里,是不是發(fā)現(xiàn)LiveData即有點像EventBus,又有點像Rxjava,這正是他的強大之處,另外它還能夠結(jié)合組件的生命周期使用,不僅能夠共享資源與數(shù)據(jù),而且UI與數(shù)據(jù)同步也是非常實時的,正如官方對Jetpack組件的描述,不僅能夠提高開發(fā)效率,而且讓應(yīng)用變得更加優(yōu)質(zhì),如果你還未體驗過,趕緊試一下吧。