說LocalBroadcastManager
有點冷落,一個是不多人知道而且合理使用廣播,不少人要麼使用的是系統的全局廣播BraoadCastRecever
,要麼使用EventBus
或RxAndroid
等等其餘觀察者模式的三方庫,慢慢的就失寵了。然鵝,並不僅是這樣,當如今你的項目遷移到Androidx時,會發現官方竟然把它廢棄了,隨後官方明確指出最適合的替代品LiveData
和reactive streams
,見如下描述 (官方傳送門)html
LocalBroadcastManager
顧名思義就是本地廣播,也是基於觀察者模式的事件總線,用於應用內通訊,比較安全和高效,雖然遷移到androidx後,在1.1.0
高版本LocalBroadcastManager
被標記過期,可是它的用處仍是蠻大的,相比系統的廣播,本地廣播有着沒法比擬的優越性,並且很是高效,相比第三方觀察者模式庫,知足條件下我寧願使用1.0.0
版本的LocalBroadcastManager
。java
LocalBroadcastManager
爲本地廣播,只能接受自身App發送的廣播,只能用於應用內之間的通訊,範圍相對較小;而系統的BraoadCastRecever
能夠實現跨進程通信,範圍更大。LocalBroadcastManager
通訊核心是Handler
,因此只能用於應用內通訊,安全和效率都很高;而系統的BraoadCastRecever
通訊核心是Binder
機制, 實現跨進程通訊,範圍更廣,致使運行效率稍微遜一點。LocalBroadcastManager
因爲核心是Handler
,並且只能動態註冊,只能用於app內通訊,安全上更加的有保障;而系統的BraoadCastRecever
容易被利用,安全上相對較弱一點。註冊廣播:react
IntentFilter filter = new IntentFilter();
filter.addAction(「你的Action」);
LocalBroadcastManager.getInstance(Context context).registerReceiver(BroadcastReceiver receiver, filter);
複製代碼
發送廣播:android
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開發手冊(正式版)_v1.0.0》中有明確指出關於廣播的使用規範問題,如下規範等級爲強制,因此咱們應用合理正確的使用廣播,遵循綠色公約,避免信息泄露和攔截意圖的風險,如下爲規範描述:安全
若是廣播僅限於應用內,則可使用
LocalBroadcastManager#sendBroadcast()
實現,避免敏感信息外泄和 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
明顯採起的是懶漢模式的單例模式,而且初始化的時候建立了一個Handler
對象,在主線程上進行工做,用於處理髮送的廣播,這就是和系統BroadCastReciver
的最大區別,系統廣播則是經過Binder
機制進行通訊的,而本地廣播採起的是Handler
機制進行通訊。bash
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
來進行協調運做的,而mReceivers
則是以receiver
做爲key,存儲的是一個接收記錄ReceiverRecord
集合,而ReceiverRecord
是用來存儲篩選器IntentFilter
與廣播接收器BroadcastReceiver
及廣播運行狀態的,可是因爲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
進行發送消息的,檢查排隊隊列是否有未發佈的MSG_EXEC_PENDING_BROADCASTS
,沒有的話發送消息。
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
發送的消息時候,會執行executePendingBroadcasts()
方法,發送廣播的時候mPendingBroadcasts
來存儲Intent
意圖與廣播接收器ReceiverRecord
,當處理廣播的時候,從mPendingBroadcasts
中取出信息存放到廣播接收器數組BroadcastRecord[]
中,經過循環調用廣播接收的抽象方法onReceive(Context context,Intent intent)
進行消息發送到前臺主線程,就這樣完成了應用內通訊。
LiveData是官方架構組件,是Jetpack衆多組件的一個,它是一個可觀察的數據持有者類。與常規的可觀察對象不一樣,LiveData是生命週期感知的,這意味着它尊重其餘應用程序組件的生命週期,好比activities
、fragments
或者services
。這種意識確保LiveData只更新處於活動生命週期狀態的應用程序組件觀察者。
LiveData認爲,若是一個觀察者的生命週期處於STARTED
或RESUMED
狀態,那麼這個觀察者(由observer類表示)就處於活動狀態。LiveData只將更新通知活動觀察者。註冊爲監視LiveData對象的非活動觀察者不會收到有關更改的通知。
您能夠註冊一個與實現LifecycleOwner
接口的對象配對的觀察者。當相應的生命週期對象的狀態更改成DESTROYED
時,此關係容許刪除觀察者。這對於活動和片斷特別有用,由於它們能夠安全地觀察LiveData對象,而不用擔憂泄漏——當activities
和fragments
的生命週期被破壞時,它們會當即取消訂閱。
UI界面與數據實時保證一致
LiveData遵循觀察者模式。當生命週期狀態發生變化時,LiveData通知觀察者Observer
對象。您能夠合併代碼來更新這些觀察者對象中的UI。您的觀察者能夠在每次發生更改時更新UI,而不是每次應用程序數據更改時都更新UI
不會形成內存泄漏
觀察者被綁定到生命週期Lifecycle
對象,並在其關聯的生命週期被destroyed
後進行清理。
當界面銷燬或者中止活動不會形成崩潰
若是觀察者的生命週期是不活動的,例如在後堆棧中的活動,那麼它不會接收任何LiveData事件。
不須要手動處理生命週期
UI組件只觀察相關數據,不中止或恢復觀察。LiveData自動管理全部這些,由於它在觀察過程當中知道相關的生命週期狀態變化。
老是保證最新的數據
若是一個生命週期變爲不活動的,它將在再次活動時接收最新的數據。例如,在後臺的活動在返回到前臺後當即接收最新的數據。
適當的配置更改
若是某個activitys
或fragments
因爲配置更改(如設備旋轉)而從新建立,它將當即接收最新可用數據。
共享資源和數據
您可使用singleton模式擴展LiveData對象來包裝系統服務,以便在您的應用程序中共享它們。LiveData對象鏈接到系統服務一次,而後任何須要該資源的觀察者均可以查看LiveData對象。有關更多信息,請參見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() | 當活動觀察者的數量從0變爲1時調用 |
void | onInactive() | 當活動觀察者的數量從1變爲0時調用 |
void | postValue(T value) | 將任務發佈到主線程以設置給定值 |
void | setValue(T value) | 設置更新數據源 |
// 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"
複製代碼
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。如今這些UI控制器負責顯示數據,而不是保存數據狀態。 將LiveData實例與特定的Activity或Fragment實例解耦,並容許LiveData對象在配置更改後仍然存在。
Observer
,該對象定義onChanged()
方法,該方法控制LiveData對象持有的數據更改時發生的狀況。一般在UI控制器中建立一個觀察者對象,例如一個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);
}
}
複製代碼
在大多數狀況下,應用程序組件的onCreate()方法是開始觀察LiveData對象的合適位置,緣由以下: 以確保系統不會從活動或fragment的onResume()方法發出冗餘調用。 確保活動或片斷具備數據,能夠在活動時當即顯示這些數據。一旦應用程序組件處於啓動狀態,它就會從它所觀察的LiveData對象接收到最近的值。只有在設置了要觀察的LiveData對象時纔會發生這種狀況。 一般,LiveData只在數據發生更改時提供更新,而且只向活動觀察者提供更新。這種行爲的一個例外是,當觀察者從非活動狀態更改成活動狀態時,也會收到更新。此外,若是觀察者第二次從非活動狀態更改成活動狀態,則只有當值自上次活動以來發生更改時,纔會接收到更新。
LifecycleOwner
對象。這訂閱觀察者對象到LiveData對象,以便在發生更改時通知它。一般將觀察者對象附加到UI控制器中,例如Activity
或Fragment
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
model.getCurrentName().observe(this, nameObserver);
複製代碼
您可使用observeForever(observer)方法註冊一個沒有關聯LifecycleOwner對象的觀察者。在這種狀況下,觀察者老是被認爲是活躍的,所以老是被通知修改。您能夠刪除調用removeObserver(Observer)方法的這些觀察者。
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對象具備活動觀察者時,將調用onActive()方法。這意味着您須要開始從該方法觀察股票價格更新。
當LiveData對象沒有任何活動的觀察者時,將調用onInactive()方法。由於沒有觀察者在聽,因此沒有理由保持與StockManager服務的鏈接。
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的一個實例)做爲第一個參數傳遞。這樣作意味着這個觀察者被綁定到與全部者關聯的Lifecycle對象上,這意味着:
若是Lifecycle對象沒有處於活動狀態,那麼即便值發生了更改,也不會調用觀察者 銷燬生命週期對象後,將自動刪除觀察者。
LiveData對象是生命週期感知的,這意味着您能夠在多個Activitys
、Fragments
和service
之間共享它們。爲了保持示例的簡單性,能夠將LiveData類實現爲單例,以下所示:
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
中能夠這麼調用,經過單例實現共享數據
public class MyFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
StockLiveData.get(symbol).observe(this, price -> {
// Update the UI.
});
}
}
複製代碼
在將LiveData對象發送給觀察者以前,您可能但願更改存儲在LiveData對象中的值,或者您可能須要根據另外一個LiveData實例的值返回另外一個LiveData實例。生命週期包提供轉換類,該類包含支持這些場景的helper方法。
Transformations.map ()
對存儲在LiveData對象中的值應用函數,並將結果向下傳播
LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
複製代碼
Transformations.switchMap()
與map()相似,將函數應用於存儲在LiveData對象中的值,並將結果解包並向下分派。傳遞給switchMap()的函數必須返回LiveData對象,以下例所示:
private LiveData<User> getUser(String id) {
...;
}
LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );
複製代碼
您可使用轉換方法在觀察者的生命週期中攜帶信息。除非觀察者正在觀察返回的LiveData對象,不然不會計算轉換。由於轉換是延遲計算的,因此與生命週期相關的行爲是隱式傳遞的,不須要額外的顯式調用或依賴關係。
若是您認爲在ViewModel對象中須要一個生命週期對象,那麼轉換多是一個更好的解決方案。例如,假設您有一個UI組件,它接受一個地址並返回該地址的郵政編碼。您能夠爲這個組件實現簡單的視圖模型,以下面的示例代碼所示:
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對象註銷註冊,並在每次調用getPostalCode()
時註冊到新實例。此外,若是從新建立UI組件,它將觸發對repository.getPostCode()
方法的另外一個調用,而不是使用前一個調用的結果。
相反,您能夠將郵政編碼查詢實現爲地址輸入的轉換,以下面的示例所示:
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字段被定義爲轉換addressInput。只要您的應用程序具備與該postalCode字段關聯的活動觀察者,就會在addressInput更改時從新計算並檢索字段的值 。
此機制容許較低級別的應用程序建立LiveData按需延遲計算的對象。甲ViewModel對象能夠容易地得到以引用LiveData的對象,而後在它們的頂部限定的變換規則。
有十幾種不一樣的特定轉換可能對您的應用有用,但默認狀況下不提供。要實現本身的轉換,您可使用MediatorLiveData該類,該類偵聽其餘LiveData對象並處理它們發出的事件。MediatorLiveData正確地將其狀態傳播到源LiveData對象。要了解有關此模式的更多信息,請參閱Transformations 該類的參考文檔 。
MediatorLiveData
是一個子類LiveData,容許您合併多個LiveData源。MediatorLiveData
只要任何原始LiveData源對象發生更改,就會觸發對象的觀察者。
例如,若是LiveDataUI中有一個能夠從本地數據庫或網絡更新的對象,則能夠將如下源添加到該 MediatorLiveData
對象:
LiveData與存儲在數據庫中的數據關聯的對象。
LiveData與從網絡訪問的數據關聯的對象。
LiveData liveData1 = ...;
LiveData liveData2 = ...;
MediatorLiveData liveDataMerger = new MediatorLiveData<>();
liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
複製代碼
讀到這裏,是否是發現LiveData
即有點像EventBus
,又有點像Rxjava
,這正是他的強大之處,另外它還可以結合組件的生命週期使用,不只可以共享資源與數據,並且UI與數據同步也是很是實時的,正如官方對Jetpack組件的描述,不只可以提升開發效率,並且讓應用變得更加優質,若是你還未體驗過,趕忙試一下吧。