Android 架构组件 - Lifycycle, LiveData, ViewModel

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 构造方法传递两个参数 ViewModelStoreFactory ,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;
    }
复制代码

总结一下:

  1. 如果使用的 support 库为 27.1.0 以上则 Activity 和 Fragment 都实现了 ViewModelStoreOwner 接口,提供ViewModelStore实例
  2. 如果使用的 support 库为 27.1.0 以下,则会创建一个 HolderFragment,同样实现了 ViewModelStoreOwner 接口,提供ViewModelStore实例
  3. 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 的内存泄漏

你可能感兴趣的:(Android 架构组件 - Lifycycle, LiveData, ViewModel)