理解Android Architecture Components系列之Lifecycle(三)

有了整体的认识,就可以对之前没有详细介绍的类做一个深入的探究。首先来看看Lifecycle。

Handling Lifecycles

android.arch.lifecycle提供的类和接口可以让你构建能感知生命周期(lifecycle-aware)的类。所谓可以感知生命周期就是能够根据Activity或者Fragment的生命周期自行调整类的行为。

Android系统中定义的大多数组件都是有生命周期的。这些组件的生命周期是由系统管理的。作为一个开发者必须遵守这些生命周期的规则,否则就会出现内存泄漏或者应用崩溃的情况。

这么说好像有点无力啊,让我们一起举个例子吧。比如我们需要在Activity中做一个对位置的监听:

class MyLocationListener {
    public MyLocationListener(Context context, Callback callback) {
        // ...
    }

    void start() {
        // connect to system location service
    }

    void stop() {
        // disconnect from system location service
    }
}

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, (location) -> {
            // update UI
        });
  }

    public void onStart() {
        super.onStart();
        myLocationListener.start();
    }

    public void onStop() {
        super.onStop();
        myLocationListener.stop();
    }
}

在Activity的onCreate()方法中创建Listener,在onStart()时开始监听,在onStop()中停止监听。看起来一切正常啊,就是可能Activty不停的切换会调用多次LocationListener的onStartonStop而已。

但是问题来了,举个例子哈。比如有些App会提供一个选项,说允不允许提供位置,那么在定位之前就需要检查这些设置。好,那么就在Activity里提供检查的代码:

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, location -> {
            // update UI
        });
    }

    public void onStart() {
        super.onStart();
        Util.checkUserStatus(result -> {
            // what if this callback is invoked AFTER activity is stopped?
            if (result) {
                myLocationListener.start();
            }
        });
    }

    public void onStop() {
        super.onStop();
        myLocationListener.stop();
    }
}

看起来好像也没有问题呀,让我们来一起分析一下这段代码。假如程序执行到super.onStart()这里还没等checkUserStatus执行完成就执行了onStop()(这里就假设检查本身回花费时间比较长吧),意识到出了什么问题了吗?myLocationListenerstop()方法在start()之前执行了,这本身在逻辑上就是不合理的。更严重的问题是:myLocationListener过一段时间开始执行start(),然后没有stop的代码被调用了。这意味着如果没有其他操作,Activity将会一直在后台监听Location的改变。

Lifecycle就是用来解决这些问题的,并且解决的非常优雅(in a resilient and isolated way)。

Lifecycle

Lifecycle是一个包含组件(Activity或者Fragment)生命周期状态的类,这个类还能够为其他类提供当前的生命周期。

Lifecycle使用两个主要的枚举来跟踪他所关联组件的生命周期。

  • Event 事件 从组件或者Lifecycle类分发出来的生命周期,它们和Activity/Fragment生命周期的事件一一对应。(ON_CREATE,ON_START,ON_RESUME,ON_PAUSE,ON_STOP,ON_DESTROY)
  • State 状态 当前组件的生命周期状态(INITIALIZED,DESTROYED,CREATED,STARTED,RESUMED)

说的有点抽象了,看一下下面的图:

image.png

具体的代码如下:

public class MyObserver implements LifecycleObserver {
   @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
   public void onResume() {
   }

   @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
   public void onPause() {
   }
}
//aLifecycleOwner一般是实现了LifecycleOwner的类,比如Activity/Fragment
aLifecycleOwner.getLifecycle().addObserver(new MyObserver());

LifecycleOwner

那什么是LifecycleOwner呢?实现LifecycleOwner就表示这是个有生命周期的类,他有一个getLifecycle ()方法是必须实现的。com.android.support:appcompat-v7:26.1.0中的AppCompatActivity已经实现了这个接口,详细的实现可以自行查看代码。

对于前面提到的监听位置的例子。可以把MyLocationListener实现LifecycleObserver,然后在LifecycleActivity/Fragment)的onCreate方法中初始化。这样MyLocationListener就能自行处理生命周期带来的问题。具体代码如下:

class MyActivity extends AppCompatActivity {
   private MyLocationListener myLocationListener;

   public void onCreate(...) {
       myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
           // update UI
       });
       Util.checkUserStatus(result -> {
           if (result) {
               myLocationListener.enable();
           }
       });
 }
}

class MyLocationListener implements LifecycleObserver {
   private boolean enabled = false;
   public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
      ...
   }

   @OnLifecycleEvent(Lifecycle.Event.ON_START)
   void start() {
       if (enabled) {
          // connect
       }
   }

   public void enable() {
       enabled = true;
       if (lifecycle.getState().isAtLeast(STARTED)) {
           // connect if not connected
       }
   }

   @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
   void stop() {
       // disconnect if connected
   }
}

简单过一下上面的代码不难发现这样可以解决文章开始提到的问题。(调用enable()时,已经处于CREATED状态,下面的代码是不会执行的)这样在如果Lifecycle没有处在合适的时机是不会调用相关回调的。现在MyLocationListener是有生命周期感知能力的(lifecycle-aware),不再依赖Activity的生命周期。

Lifecycles的最佳建议

  • 保持UI Controllers(Activity/Fragment)中代码足够简洁。一定不能包含如何获取数据的代码,要通过ViewModel获取LiveData形式的数据。
  • 用数据驱动UI,UI的职责就是根据数据改变显示的内容,并且把用户操作UI的行为传递给ViewModel。
  • 把业务逻辑相关的代码放到ViewModel中,把ViewModel看成是链接UI和App其他部分的胶水。但ViewModel不能直接获取数据,要通过调用其他类来获取数据。
  • 使用DataBinding来简化View(布局文件)和UI Controllers(Activity/Fragment)之间的代码
  • 如果布局本身太过复杂,可以考虑创建一个Presenter类来处理UI相关的改变。虽然这么做会多写很多代码,但是对于保持UI的简介和可测试性是有帮助的。
  • 不要在ViewModel中持有任何View/Activity的context。否则会造成内存泄露。

附加说明

在 Support Library 26.1.0中Activity/Fragment已经实现了LifecycleOwner接口。
如果想在自定义的类中实现LifecyclerOwner,就需要用到LifecycleRegistry,并且需要自行发送Event。

相关文章:
理解Android Architecture Components系列(一)
理解Android Architecture Components系列(二)
理解Android Architecture Components系列之Lifecycle(三)
理解Android Architecture Components系列之LiveData(四)
理解Android Architecture Components系列之ViewModel(五)
理解Android Architecture Components系列之Room(六)
理解Android Architecture Components系列之Paging Library(七)
理解Android Architecture Components系列之WorkManager(八)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容