Android Architecture Components 提供从管理 UI 组件的生命周期到处理数据持久性的一系列库,来帮助您设计健壮,易测试和维护的 App。
本文将简单介绍 Lifcycle, LiveData, ViewModel 的使用并对一些重要的源码进行分析
集成
请参阅官方文档 在项目中添加组件
Lifecycle
介绍
假设我们这里有一个具有生命周期的组件 A (例如 Activity 或 Fragment),而另一个组件 B 需要响应 A 组件的生命周期,传统的方式是在组件 A 的生命周期依赖组件 B,但是这种方式导致代码健壮性较低,同时易导致一系列的错误。使用 Lifecycle 组件,您可以将组件 B 的代码从组件 A 的生命周期方法中移到组件本身
Lifecycle 的使用
以 MyLocationManager 为例,添加相关生命周期的注解。
class MyLocationManager : LifecycleObserver {
@OnLifecycleEvent(ON_CREATE)
fun onCreate() {
glog("location onCreate")
}
@OnLifecycleEvent(ON_START)
fun onStart() {
glog("locatoin onstart")
}
@OnLifecycleEvent(ON_DESTROY)
fun onDestroy() {
glog("locatoin ondestory")
}
}
复制代码
然后创建 MyLocationManager 实例,然后就可以监听到 Activity 或 Fragment 的生命周期变化。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val locationManager = LocationManager()
lifecycle.addObserver(locationManager)
}
复制代码
addObserver 这个方法有一个小细节需要注意一下:
/**
* Adds a LifecycleObserver that will be notified when the LifecycleOwner changes
* state.
*
* The given observer will be brought to the current state of the LifecycleOwner.
* For example, if the LifecycleOwner is in {@link State#STARTED} state, the given observer
* will receive {@link Event#ON_CREATE}, {@link Event#ON_START} events.
*
* @param observer The observer to notify.
*/
@MainThread
public abstract void addObserver(@NonNull LifecycleObserver observer);
复制代码
如果 LifecycleOwner 当前的状态是 STATED,当我们调用 addObserver 方法时,会接收到 ON_CREATE 和 ON_START events.
以 MyLocationManager 为例说明,当我们在 Activity 的 onResume 方法中 addObserver 时,MyLocationManager 仍然会执行 onCreate 和 onStart 方法.
UML 类图
Event and State
Even 和 State 是 Lifecycle 中很重要的概念,结合自己的代码与 google 官方的配图相信应该很容易理解.
关键源码
我们在查看源码时发现 Lifecycle 只在 Activity 的 onSaveInstanceState 中 调用了 mLifecycleRegistry.markState(Lifecycle.State.CREATED) 方法,在其他生命周期中并没有 Lifecycle 的相关代码,那 support 库中是怎么做到 disptch event 的呢?
原来在 onCreate 中有一行关键代码,它创建了一个不可见的 fragment 去分发 event
ReportFragment.injectIfNeededIn(this);
复制代码
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
dispatchCreate(mProcessListener);
dispatch(Lifecycle.Event.ON_CREATE);
}
...
@Override
public void onDestroy() {
super.onDestroy();
dispatch(Lifecycle.Event.ON_DESTROY);
// just want to be sure that we won't leak reference to an activity
mProcessListener = null;
}
复制代码
思考一下 google 为什么要采用这样的方式,而不是直接在 Activity 的生命周期去直接分发 event 呢? 以刚才的 MyLocationManager
为例, 如果打印 Activity 和 MyLocationManager 的完整生命周期日志,会得到以下结果:
05-25 06:59:17.382 32431-32431/com.slyser.arc D/arc: activity onCreate
location onCreate
05-25 06:59:17.385 32431-32431/com.slyser.arc D/arc: activity onStart
location onStart
05-25 06:59:17.386 32431-32431/com.slyser.arc D/arc: activity onResume
location onResume
05-25 06:59:36.675 32431-32431/com.slyser.arc D/arc: location onStop
activity onStop
location onDestroy
05-25 06:59:36.676 32431-32431/com.slyser.arc D/arc: activity onDestroy
复制代码
可以看到进入 Activity 时的执行顺序: Activity -> MyLocationManager. 退出 Activity 时的执行顺序刚好相反: MyLocationManager -> Activity. 而直接在 Activity 的生命周期中直接分发 event 是达不到这种效果的。
LiveData
介绍
LiveData 是一个可观察的数据持有者。与常规可观察性不同,LiveData 具有生命周期感知能力,这意味着它尊从其他应用程序组件(例如 Activity, Fragment, Service)的生命周期。 这种设计确保 LiveData 只更新处于活动生命周期状态的应用程序组件观察者。如果观察者的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会将观察者视为活动状态。LiveData 仅将更新通知给活跃的观察者,未注册和非活动的观察者不会收到有关更新的通知。
LiveData 的使用
首先我们先创建一个 LiveData
,然后一个简单的方法调用,我们在监听数据变化时传入了两个参数,前者 owner 用于将 livedata 与生命周期绑定,后者监听数据的变化,这样你就能使用 LiveData 了
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer)
复制代码
class MainActivity : AppCompatActivity() {
val liveData = MutableLiveData()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
liveData.observe({this@MainActivity.lifecycle}, {
it?.let { glog(it.name) }
})
findViewById
关键源码
之前介绍概念中有一段这样的话很重要:
如果观察者的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会将观察者视为活动状态。LiveData 仅将更新通知给活跃的观察者。
怎么理解这段话呢,执行如下代码,看下日志的打印内容:
class MainActivity : AppCompatActivity() {
private val person : Person = Person("live data onCreate")
val liveData = MutableLiveData()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
liveData.observe({this@MainActivity.lifecycle}, {
it?.let { glog(it.name) }
})
liveData.value = person
}
override fun onStart() {
super.onStart()
person.name = "live data onStart"
}
data class Person(var name:String)
}
复制代码
日志打印出的 name 是 live data onStart, 在 onCreate 方法中明明改变了 LiveData 的值,为什么打印的 name 不是 live data onCreate 呢?
原来我们在虽然在 onCreate 中调用了 setValue 方法,但是此时 LiveData 的 active 状态为 false 并不会通知数据变化.
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++; // 管理数据变化的版本
mData = value;
dispatchingValue(null);
}
复制代码
private void dispatchingValue(@Nullable ObserverWrapper initiator) {
...
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
复制代码
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) { // 在 onCreate 中此时的活动状态为 false
return;
}
...
if (observer.mLastVersion >= mVersion) {
return; //active 状态变化了,但是数据版本没有增加,不会调用 onChange
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
}
复制代码
然后当 Activity 的生命执行到 onStart 后,此时 active 状态变化为 true,最终调用 observer.mObserver.onChanged((T) mData),通知 LiveData 数据变化,但此时 person 的 name 已经变为 live data onStart 了.
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
...
if (mActive) {
dispatchingValue(this);
}
}
复制代码
Transformations
Transformations 可以让 LiveData 在不同数据类型间进行变换,相关代码如下:
LiveData userLiveData = ...;
LiveData userName = Transformations.map(userLiveData, user -> {
return user.firstName + " " + user.lastName
});
MutableLiveData userIdLiveData = ...;
LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
repository.getUserById(id));
void setUserId(String userId) {
this.userIdLiveData.setValue(userId);
}
复制代码
ViewModel
介绍
ViewModel 目的在于以生命周期的形式存储和管理与 UI 相关的数据。 ViewModel 允许数据在配置变化(例如屏幕旋转)后仍然存活。
生命周期
ViewModel 的使用
ViewModel 的使用很简单,创建一个类继承 ViewModel
class UserViewModel : ViewModel() {
}
复制代码
如果你想在 ViewModel 中使用 Context,可以继承 AndroidViewModel,然后通过一行代码即可得到 ViewModel 对象
val viewModel = ViewModelProviders.of(this).get(UserViewModel::class.java)
复制代码
关键源码
首先看ViewModelProviders.of
的相关代码
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
复制代码
ViewModelProviders.of
创建了一个 ViewModelProvider
对象,ViewModelProvider
构造方法传递两个参数 ViewModelStore
和 Factory
,Factory 很简单,就是让你自己决定如何创建 ViewModel,如果 factory 为 null 则使用 android 提供的默认实现,ViewModelStore
简单来说就是个存放 ViewModel
对象的 map,key 默认为类名.
@NonNull
@MainThread
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
return holderFragmentFor(activity).getViewModelStore(); // 兼容27.1.0以下版本的 support 库
}
复制代码
其中ViewModelStoreOwner
是一个接口,只有一个方法,在 27.1.0 的 FragmentActivity 已经实现了该接口
ViewModelStore getViewModelStore();
复制代码
27.1.0 以下的版本 google 则通过创建一个不可见的实现 ViewModelStoreOwner
接口的 fragment 去做兼容,以下是相关关键代码
HolderFragment holderFragmentFor(FragmentActivity activity) {
FragmentManager fm = activity.getSupportFragmentManager();
HolderFragment holder = findHolderFragment(fm);
if (holder != null) {
return holder;
}
holder = mNotCommittedActivityHolders.get(activity);
if (holder != null) {
return holder;
}
if (!mActivityCallbacksIsAdded) {
mActivityCallbacksIsAdded = true;
activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks); //注册生命周期,activity 销毁时将 HolderFragment 从 mNotCommittedActivityHolders 移除
}
holder = createHolderFragment(fm);
mNotCommittedActivityHolders.put(activity, holder);
return holder;
}
复制代码
搞清楚了ViewModelStore
再回过头来看ViewModelProvider
的 get 方法
@NonNull
@MainThread
public T get(@NonNull String key, @NonNull Class modelClass) {
ViewModel viewModel = mViewModelStore.get(key); // mViewModelStore为构造方法传递
if (modelClass.isInstance(viewModel)) { //store 中存储了 ViewModel 则直接返回
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass); //store 中没有 ViewModel 则创建一个 ViewModel,并在 ViewModelStore 中的 map 中存放
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
复制代码
总结一下:
- 如果使用的 support 库为 27.1.0 以上则 Activity 和 Fragment 都实现了
ViewModelStoreOwner
接口,提供ViewModelStore
实例 - 如果使用的 support 库为 27.1.0 以下,则会创建一个 HolderFragment,同样实现了
ViewModelStoreOwner
接口,提供ViewModelStore
实例 ViewModelStore
本质是默认 key 为类名 value 为 ViewModel 的 map
在 fragment 间共享数据
我们在不同的 fragment 中调用 ViewModelProviders.of()
时,如果参入的参数为 activity,则获取的 ViewModel 对象为同一实例,代码如下:
val viewModel = ViewModelProviders.of(getActivity()).get(UserViewModel::class.java)
复制代码
这样我们就可以在两个或更多 fragment 间彼此间进行通信。
最佳实践
- 尽可能保持您的 UI 控制器(activity 和 fragment)精简。他们不应该试图获取他们自己的数据;相反,使用 ViewModel 来做到这一点,并通过监听 LiveData 对象来更新视图。
- 尝试编写数据驱动的用户界面,其中您的 UI 控制器的职责是在数据更改时更新视图,或将用户操作通知给ViewModel。
- 把你的数据逻辑放在 ViewModel 类中。 ViewModel 应作为您的 UI 控制器和其他应用程序之间的连接器。 但要小心,ViewModel 不负责提取数据(例如,来自网络)。 相反,ViewModel 应调用相应的组件来获取数据,然后将结果提供给UI控制器
- 使用 Data Binding 在视图和 UI 控制器之间保持干净的界面。 这可以使您的视图更具说明性,并最大限度地减少需要在 activity 和 fragment 中编写的更新代码。 如果你喜欢用 java 编程语言来做到这一点,可以使用像 Butter Knife 这样的库来避免样板代码并且能够更好的抽象。
- 如果您的 UI 很复杂,请考虑创建一个 presenter 来处理 UI 修改。这可能是一项艰巨的任务,但它可以使您的 U I组件更易于测试。
- 避免在 ViewModel 中引用 View 或 Activity 上下文。如果 ViewModel 存活的时间比 Activity(在配置更改的情况下),将会造成 activity 的内存泄漏