[关闭]
@Tyhj 2020-03-26T13:15:03.000000Z 字数 9024 阅读 994

Lifecycles组件使用

Android


Android JetPack简介

Jetpack 是Google官方的一套库、工具和指南,可帮助开发者更轻松地编写优质应用。这些组件可帮助您遵循最佳做法、让您摆脱编写样板代码的工作并简化复杂任务,以便您将精力集中放在所需的代码上。

Jetpack 包含与平台 API 解除捆绑的 androidx.* 软件包库。这意味着,它可以提供向后兼容性,且比 Android 平台的更新频率更高,以此确保您始终可以获取最新且最好的 Jetpack 组件版本。

Android Jetpack官方网站

Lifecycles介绍

Lifecycles是JetPack中的一个组件,主要功能就是可以监听Activity和Fragment的生命周期;作用就是可以将一些在Activity中,和Activity生命周期相关的操作下放到具体的组件功能中去;目的在于减少Activity和其它组件的耦合,这些组件可以根据 Activity 或 Fragment 的当前生命周期状态自动调整其行为。

简单的使用

使用是非常简单的,ShowUserActivity继承AppCompatActivity,调用getLifecycle()方法就可以获取到Lifecycle对象然后添加生命周期监听,onStateChanged方法会返回生命周期的变化值

  1. //注册对Activity的生命周期变化的监听
  2. ShowUserActivity.this.getLifecycle().addObserver(new LifecycleEventObserver() {
  3. @Override
  4. public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
  5. switch (event) {
  6. case ON_CREATE:
  7. case ON_START:
  8. case ON_RESUME:
  9. case ON_PAUSE:
  10. case ON_STOP:
  11. case ON_DESTROY:
  12. case ON_ANY:
  13. default:
  14. break;
  15. }
  16. }
  17. });

原理分析

ShowUserActivity继承关系->AppCompatActivity->FragmentActivity->ComponentActivity;看getLifecycle()方法是ComponentActivity提供的方法,而这个方法是ComponentActivity实现LifecycleOwner接口里面的方法;方法返回的是一个LifecycleRegistry对象,是ComponentActivity的一个成员变量,但是在ComponentActivity类的生命周期里面并没有做什么关于操作mLifecycleRegistry的操作,那生命周期变化监听的接口调用应该不是这里触发的;

  1. //ComponentActivity
  2. private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
  3. @Override
  4. public Lifecycle getLifecycle() {
  5. return mLifecycleRegistry;
  6. }

那再看FragmentActivity类,里面重写了getLifecycle方法,而且在每个生命周期里面都调用了mFragmentLifecycleRegistry.handleLifecycleEvent方法,看一下这个方法的实现,猜也能猜到最后调用了mLifecycleObserver.onStateChanged(owner, event);方法,跟着源码看下去的确是这样,该方法在LifecycleRegistry里面,这里代码没有给出,为了防止出错,中间也经过了一系列比较复杂的判断;不过找了很久都没有找到ON_ANY这个标准的返回是在哪里触发的,这个感觉是没用的;

  1. //FragmentActivity
  2. final LifecycleRegistry mFragmentLifecycleRegistry = new LifecycleRegistry(this);
  3. @Override
  4. public Lifecycle getLifecycle() {
  5. // Instead of directly using the Activity's Lifecycle, we
  6. // use a LifecycleRegistry that is nested exactly outside of
  7. // when Fragments get their lifecycle changed
  8. // TODO(b/127528777) Drive Fragment Lifecycle with LifecycleObserver
  9. return mFragmentLifecycleRegistry;
  10. }
  11. ...
  12. protected void onStart() {
  13. ...
  14. mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
  15. }
  16. ...

其实仔细看还是比较简单的,就是写了一个LifecycleEventObserver接口,提供一个返回Activity生命周期状态的方法,然后在Activity的父类里面提供一个LifecycleRegistry对象,这个对象维护一个LifecycleEventObserver接口的集合,可以注册监听接口进来添加到集合里面,当生命周期变化的时候,拿到集合中的LifecycleEventObserver接口对象,调用onStateChanged方法,返回当前的生命周期;

  1. //LifecycleEventObserver接口
  2. public interface LifecycleEventObserver extends LifecycleObserver {
  3. /**
  4. * Called when a state transition event happens.
  5. *
  6. * @param source The source of the event
  7. * @param event The event
  8. * 返回Activity生命周期的状态
  9. */
  10. void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event);
  11. }

扩展应用

原理知道了,其实自己去实现也很简单;我们想要实现类似的功能,不只是监听Activity的生命周期,可以监听任何一个组件的信息,在这个基础上实现也是比较简单的,只需要类继承LifecycleOwner接口就可以了,当然这里面我们还是使用了LifecycleRegistry实现,所以返回的生命周期数据也只能是Activity生命周期对应的Lifecycle.Event对象,如果想要实现返回其他类型的生命周期,重写一下LifecycleRegistry就可以了

  1. public class LifecycleDog implements LifecycleOwner {
  2. private LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
  3. @NonNull
  4. @Override
  5. public Lifecycle getLifecycle() {
  6. return lifecycleRegistry;
  7. }
  8. /**
  9. * 模拟生命周期
  10. */
  11. private void onStart(){
  12. lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
  13. }
  14. /**
  15. * 模拟生命周期
  16. */
  17. private void onStop(){
  18. lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
  19. }
  20. }

具体应用

举个例子,一个界面需要获取用户信息进行展示,以MVP架构为例,首先是抽象出 View层和Presenter层的接口,View层的功能就是展示用户信息,所以抽象出展示用户信息的接口,用于给P层调用;Presenter层功能是从Model层取用户信息,所有抽象出获取用户信息的接口,用于给View层调用;Model层是获取用户信息,不管是从网络还是数据库取,先不用管

  1. public interface ShowUserContract {
  2. /**
  3. * 显示用户信息抽象类
  4. */
  5. interface IShowUserView {
  6. /**
  7. * 展示用户信息
  8. *
  9. * @param userInfo
  10. */
  11. void showUserInfo(UserInfo userInfo);
  12. }
  13. /**
  14. * 获取用户信息抽象类
  15. */
  16. interface IShowUserPresenter {
  17. /**
  18. * 获取用户信息
  19. */
  20. void getUserInfo();
  21. }
  22. }

然后在View层,也就是Activity里面去实现接口,在Activity启动的时候取获取用户信息,调用P层的接口取获取用户信息,进行展示

  1. public class ShowUserActivity extends AppCompatActivity implements ShowUserContract.IShowUserView {
  2. TextView mTextView;
  3. /**
  4. * P层的引用
  5. */
  6. private ShowUserPresenter presenter = new ShowUserPresenter(this);
  7. @Override
  8. protected void onCreate(Bundle savedInstanceState) {
  9. super.onCreate(savedInstanceState);
  10. setContentView(R.layout.activity_main);
  11. mTextView = findViewById(R.id.tvName);
  12. //获取用户信息
  13. presenter.getUserInfo();
  14. }
  15. @Override
  16. public void showUserInfo(UserInfo userInfo) {
  17. if (userInfo != null) {
  18. //展示用户信息
  19. mTextView.setText(userInfo.getName());
  20. }
  21. }
  22. }

在P层,同样也实现获取用户信息的接口

  1. public class ShowUserPresenter implements ShowUserContract.IShowUserPresenter {
  2. /**
  3. * View层的引用
  4. */
  5. private ShowUserContract.IShowUserView mIShowUserView;
  6. /**
  7. * M层的引用
  8. */
  9. private UserInfoModel mUserInfoModel;
  10. public ShowUserPresenter(ShowUserContract.IShowUserView IShowUserView) {
  11. mIShowUserView = IShowUserView;
  12. mUserInfoModel = new UserInfoModel();
  13. }
  14. @Override
  15. public void getUserInfo() {
  16. //从M层获取用户信息
  17. UserInfo userInfo = mUserInfoModel.getUserInfo();
  18. //调用View层的接口进行信息展示
  19. mIShowUserView.showUserInfo(userInfo);
  20. }
  21. }

这样就实现了一个简单的展示用户信息的功能,如果获取用户信息是一个耗时接口,就会有问题,这里做一个线程处理,在子线程获取用户信息,在主线程进行返回;

  1. @Override
  2. public void getUserInfo() {
  3. AppExecutors.getInstance().getNetworkIo().execute(()->{
  4. //从M层获取用户信息
  5. UserInfo userInfo = mUserInfoModel.getUserInfo();
  6. AppExecutors.getInstance().getMainThread().execute(()->{
  7. //调用View层的接口进行信息展示
  8. mIShowUserView.showUserInfo(userInfo);
  9. });
  10. });
  11. }

这时候如果当这个Activity关闭了,获取操作还没有执行完,会导致什么情况?讲道理View被销毁了,这里是不是应该出现空指针错误,是不是要加判断,判断一下mIShowUserView是否为空;

  1. @Override
  2. public void getUserInfo() {
  3. AppExecutors.getInstance().getNetworkIo().execute(()->{
  4. //从M层获取用户信息
  5. UserInfo userInfo = mUserInfoModel.getUserInfo();
  6. SystemClock.sleep(5000);
  7. AppExecutors.getInstance().getMainThread().execute(()->{
  8. //调用View层的接口进行信息展示
  9. Log.e("ShowUserPresenter","showUserInfo");
  10. //判断View是否为空
  11. if(mIShowUserView!=null){
  12. mIShowUserView.showUserInfo(userInfo);
  13. }
  14. });
  15. });
  16. }

其实这是有问题的,因为在线程里面持有mIShowUserView对象的强引用,线程没有结束mIShowUserView是不会被释放的,只是会造成内存泄漏;还有个问题,View持有Presenter的引用,Presenter也持有View的引用;在循环引用的情况,如果JVM的回收方式是引用计数法,那么也会造成两个对象都无法被回收,所以即使获取数据的线程结束了,对象也没法被回收;但是如果JVM的回收方式是可达性分析,那么不存在循环引用回收不掉问题,在这个例子里面问题不是特别大,线程结束后对象自然会被释,但是如果是一个会被反复打开的Activity或者线程的执行操作很长,就会造成更严重的内存泄漏;目前大多数JVM回收方式都是引用计数法

为了避免内存泄漏,界面被关闭就需要去手动释放掉Presenter里面的View对象,与此同时,Presenter调用View的接口的时候就需要判断View是否为空了;在Presenter中添加释放View的代码

  1. @Override
  2. public void destroy() {
  3. mIShowUserView=null;
  4. mUserInfoModel=null;
  5. }
  6. }

当Activity被释放的时候调用该方法

  1. @Override
  2. protected void onDestroy() {
  3. super.onDestroy();
  4. //对presenter进行释放
  5. presenter.destroy();
  6. }

Presenter对象的释放在View中进行,View层其实只应该关心界面交互,其实这个操作对View本身来说是不应该关心的,操作较多了也会导致View层的代码比较的复杂难以维护;如果使用Lifecycles就可以让Presenter自己去处理资源释放的问题;只需要修改一下Presenter的构造函数就可以了,监听到Activity销毁的时候进行资源释放;

  1. public ShowUserPresenter(ShowUserContract.IShowUserView IShowUserView,Lifecycle mLifecycle) {
  2. mIShowUserView = IShowUserView;
  3. mUserInfoModel = new UserInfoModel();
  4. mLifecycle.addObserver(new LifecycleEventObserver() {
  5. @Override
  6. public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
  7. if(event==Lifecycle.Event.ON_DESTROY){
  8. destroy();
  9. }
  10. }
  11. });
  12. }

同样View层使用的时候传入Lifecycle对象,也不需要再去释放Presenter;

  1. /**
  2. * P层的引用
  3. */
  4. private ShowUserPresenter presenter = new ShowUserPresenter(this,getLifecycle());

只是一个非常简单的例子,在实际的编码中,还是非常有用的;

深入使用

上面的例子,虽然解决了在View层释放Presenter的问题,但是我们的Presenter中还是会有很多的判断View是否为空的操作,这种代码无脑重复,又不能不写,感觉太冗余了;利用Lifecycles其实也是可以解决的,实现稍微复杂一点,但是用起来就比较舒服了;

现在我们模拟实现一下Model层获取数据的操作,一般获取数据,我们也会定义一个数据返回监听的接口,定义的比较简单,就是一个泛型的数据返回

  1. public interface IDataBackListener<T> {
  2. /**
  3. * 获取到数据
  4. *
  5. * @param t
  6. */
  7. void dataBack(T t);
  8. /**
  9. * 返回出错信息
  10. *
  11. * @param code
  12. * @param msg
  13. */
  14. void error(int code, String msg);
  15. }

M层的实现如下

  1. public class UserInfoModel {
  2. /**
  3. * 获取用户ID
  4. *
  5. * @return
  6. */
  7. public void getUserInfo(IDataBackListener<UserInfo> listener) {
  8. //模拟耗时操作
  9. SystemClock.sleep(5000);
  10. UserInfo userInfo=new UserInfo();
  11. //返回用户数据
  12. listener.dataBack(userInfo);
  13. }
  14. }

Presenter的获取用户信息的实现方法修改一下,为了好看,我把线程切换去掉了,到这一步代码看起来比之前复杂,是因为刚才没认真写M层的方法,现在是正常代码

  1. @Override
  2. public void getUserInfo() {
  3. //从M层获取用户信息
  4. mUserInfoModel.getUserInfo(new IDataBackListener<UserInfo>() {
  5. @Override
  6. public void dataBack(UserInfo userInfo) {
  7. //调用View层的接口进行信息展示
  8. Log.e("ShowUserPresenter", "showUserInfo");
  9. //判断View是否为空
  10. if (mIShowUserView != null) {
  11. mIShowUserView.showUserInfo(userInfo);
  12. }
  13. }
  14. @Override
  15. public void error(int code, String msg) {
  16. }
  17. });
  18. }

想要这里不判空,方法只有一个,监听到View被销毁的时候,取消dataBack方法的调用就可以了;那么感觉是需要在UserInfoModel里面操作,P层倒是无所谓,但是M层只是数据层,我感觉不应该牵扯到这些业务;可以这样做,写一个抽象类,继承IDataBackListener数据返回监听接口;在这个类里面,M层调用已实现方法返回数据,P层实现抽象方法接收数据;构造函数传入Lifcycle对象,设置生命周期变化监听,当View被销毁时,设置标志位为false,P层的方法就不会被调用,从而解决了当数据返回View是否还存在问题;

  1. public abstract class BaseDataBackListener<T> implements IDataBackListener<T> {
  2. /**
  3. * 操作终止
  4. */
  5. private boolean broken = false;
  6. public BaseDataBackListener(Lifecycle lifecycle) {
  7. lifecycle.addObserver(new LifecycleEventObserver() {
  8. @Override
  9. public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
  10. if (event == Lifecycle.Event.ON_DESTROY) {
  11. broken = true;
  12. }
  13. }
  14. });
  15. }
  16. @Override
  17. public void dataBack(T t) {
  18. if (!broken) {
  19. backData(t);
  20. }
  21. }
  22. @Override
  23. public void error(int code, String msg) {
  24. if (!broken) {
  25. backError(code, msg);
  26. }
  27. }
  28. /**
  29. * 返回数据
  30. *
  31. * @param t
  32. */
  33. public abstract void backData(T t);
  34. /**
  35. * 返回错误
  36. *
  37. * @param code
  38. * @param msg
  39. */
  40. public abstract void backError(int code, String msg);
  41. }

P层使用的时候传入Lifecycle对象即可

  1. @Override
  2. public void getUserInfo() {
  3. //从M层获取用户信息
  4. mUserInfoModel.getUserInfo(new BaseDataBackListener<UserInfo>(mLifecycle) {
  5. @Override
  6. public void backData(UserInfo userInfo) {
  7. //调用View层的接口进行信息展示
  8. Log.e("ShowUserPresenter", "showUserInfo");
  9. mIShowUserView.showUserInfo(userInfo);
  10. }
  11. @Override
  12. public void backError(int code, String msg) {
  13. }
  14. });
  15. }

这里不仅仅是减少判空问题,是在View销毁后彻底停止了P层的无效的操作,感觉还是比较有意思的

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注