首先放一张官方推荐的app设计架构图,想要了解更多(芝麻之门)
LocalBroadcastManager
冷落的LBM
说LocalBroadcastManager
有点冷落,一个是很少人知道并且合理使用广播,很多人要么使用的是系统的全局广播BraoadCastRecever
,要么使用EventBus
或RxAndroid
等等其他观察者模式的三方库,慢慢的就失宠了。然鹅,并不只是这样,当现在你的项目迁移到Androidx时,会发现官方居然把它废弃了,随后官方明确指出最适合的替代品LiveData
和reactive streams
,见以下描述 (官方传送门)
关于LBM
LocalBroadcastManager
顾名思义就是本地广播,也是基于观察者模式的事件总线,用于应用内通信,比较安全和高效,虽然迁移到androidx后,在1.1.0
高版本LocalBroadcastManager
被标记过时,但是它的用处还是蛮大的,相比系统的广播,本地广播有着无法比拟的优越性,而且非常高效,相比第三方观察者模式库,满足条件下我宁愿使用1.0.0
版本的LocalBroadcastManager
。
与系统广播的区别
- 范围上:
LocalBroadcastManager
为本地广播,只能接受自身App发送的广播,只能用于应用内之间的通信,范围相对较小;而系统的BraoadCastRecever
可以实现跨进程通讯,范围更大。 - 效率上:
LocalBroadcastManager
通信核心是Handler
,所以只能用于应用内通信,安全和效率都很高;而系统的BraoadCastRecever
通信核心是Binder
机制,
实现跨进程通信,范围更广,导致运行效率稍微逊一点。 - 安全上:
LocalBroadcastManager
由于核心是Handler
,而且只能动态注册,只能用于app内通信,安全上更加的有保障;而系统的BraoadCastRecever
容易被利用,安全上相对较弱一点。
使用方式
注册广播:
IntentFilter filter = new IntentFilter();
filter.addAction(“你的Action”);
LocalBroadcastManager.getInstance(Context context).registerReceiver(BroadcastReceiver receiver, filter);
发送广播:
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
机制进行通信。
private final HashMap> mReceivers = new HashMap<>();
private final HashMap> mActions = new HashMap<>();
private final ArrayList mPendingBroadcasts = new ArrayList<>();
public void registerReceiver(@NonNull BroadcastReceiver receiver,
@NonNull IntentFilter filter) {
synchronized (mReceivers) {
ReceiverRecord entry = new ReceiverRecord(filter, receiver);
ArrayList filters = mReceivers.get(receiver);
if (filters == null) {
filters = new ArrayList<>(1);
mReceivers.put(receiver, filters);
}
filters.add(entry);
for (int i=0; i entries = mActions.get(action);
if (entries == null) {
entries = new ArrayList(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 categories = intent.getCategories();
==========================省略部分===================
if (receivers != null) {
for (int i=0; i
明显看出本地广播是通过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
当工作线程接收到Handler
发送的消息时候,会执行executePendingBroadcasts()
方法,发送广播的时候mPendingBroadcasts
来存储Intent
意图与广播接收器ReceiverRecord
,当处理广播的时候,从mPendingBroadcasts
中取出信息存放到广播接收器数组BroadcastRecord[]
中,通过循环调用广播接收的抽象方法onReceive(Context context,Intent intent)
进行消息发送到前台主线程,就这样完成了应用内通信。
初识LiveData
以下翻译可能有槽点,去官网查看更多更详细的信息(芝麻之门)
介绍
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"
- 创建LiveData实例来保存特定类型的数据。通常会结合
ViewModel
一起使用,也可以单独使用。
public class NameViewModel extends ViewModel {
// Create a LiveData with a String
private MutableLiveData currentName;
public MutableLiveData getCurrentName() {
if (currentName == null) {
currentName = new MutableLiveData();
}
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 nameObserver = new Observer() {
@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只在数据发生更改时提供更新,并且只向活动观察者提供更新。这种行为的一个例外是,当观察者从非活动状态更改为活动状态时,也会收到更新。此外,如果观察者第二次从非活动状态更改为活动状态,则只有当值自上次活动以来发生更改时,才会接收到更新。
- 使用observe()方法将观察者对象附加到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)方法的这些观察者。
- LiveData扩展用法
public class StockLiveData extends LiveData {
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 myPriceListener = ...;
myPriceListener.observe(this, price -> {
// Update the UI.
});
}
}
方法将fragment(它是LifecycleOwner的一个实例)作为第一个参数传递。这样做意味着这个观察者被绑定到与所有者关联的Lifecycle对象上,这意味着:
如果Lifecycle对象没有处于活动状态,那么即使值发生了更改,也不会调用观察者
销毁生命周期对象后,将自动删除观察者。
LiveData对象是生命周期感知的,这意味着您可以在多个Activitys
、Fragments
和service
之间共享它们。为了保持示例的简单性,可以将LiveData类实现为单例,如下所示:
public class StockLiveData extends LiveData {
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实例的值返回另一个LiveData实例。生命周期包提供转换类,该类包含支持这些场景的helper方法。
Transformations.map ()
对存储在LiveData对象中的值应用函数,并将结果向下传播
LiveData userLiveData = ...;
LiveData userName = Transformations.map(userLiveData, user -> {
user.name + " " + user.lastName
});
Transformations.switchMap()
与map()类似,将函数应用于存储在LiveData对象中的值,并将结果解包并向下分派。传递给switchMap()的函数必须返回LiveData对象,如下例所示:
private LiveData getUser(String id) {
...;
}
LiveData userId = ...;
LiveData 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 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 addressInput = new MutableLiveData();
public final LiveData 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 该类的参考文档 。
合并多个LiveData源
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组件的描述,不仅能够提高开发效率,而且让应用变得更加优质,如果你还未体验过,赶紧试一下吧。