Android架构组件学习之LifeCycle

英文源址 Android架构组件之lifecyle
Lifecycle-aware(生命周期感知型)组件能够响应其他组件的生命周期变化,例如activitiy和fragment.这样的组件能够让你更好的组织代码,而且他们显得更轻量,更容易维护.

一个常见的场景就是,我们需要在activitiy或是fragment中在生命周期方法中实现相关的组件方法.总的来说,这种情况导致了代码在组织上比较差,而且会导致一些分散的错误.通过使用生命周期感知型组件,你可以将这些在生命周期方法中实现的代码移除,在组件内部实现即可(更大程度的实现了组件之间的解耦合);

android.arch.lifecycle 这个包里面,Android 为我们提供了可以让你构建生命周期感知型的组件的 类和相关接口.

备注:

如果想使用android.arch.lifecycle 包里面的功能详见adding components to your project.

Android framework里面的大多数APP组件都是带有生命周期熟悉的.你程序里面的组件生命周期都是被操作系统或是framework(框架层,俗称中间件)来管理的.这些组件对于你的APP运行稳定来说至关重要.不应该对这些组件进行操作,因为可能会导致内存泄露,严重的话还会导致程序崩溃.

想象一下我们有一个显示位置的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;

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

    @Override
    public void onStart() {
        super.onStart();
        myLocationListener.start();
        // manage other components that need to respond
        // to the activity lifecycle
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
        // manage other components that need to respond
        // to the activity lifecycle
    }
}

尽管上述代码看起来并没啥问题,但是在实际开发中为了响应当前生命周期变化,你需要添加很多额外代码来管理UI接其他组件.在例如

onStart()onStop() 这样的生命周期中管理多个组件让维护显得很困难.此外,我们在activitiy或是fragment已经是stop前并不能保证组件启动.这种情况很常见,在我们需要一个长时间运行的操作时候,例如在onStart() 方法中进行参数校验工作.如果 onStop()onStart() 之前结束了,这样会导致一个竞争条件(多线程,或者因执行顺序导致数据结果).该情况的出现会让这个组件(myLocationListener)生命周期超出其需要的时间.

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, location -> {
            // update UI  老子活了

        });
    }

    @Override
    public void onStart() {
        super.onStart();
        Util.checkUserStatus(result -> {
            // what if this callback is invoked AFTER activity is stopped?
            //添加耗时的异步操作 ?
            if (result) {
                myLocationListener.start();//myLocationListener 老子又回来啦
            }
        });
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();//老子挂了
    }
}

android.arch.lifecycle 包提供了一些类和接口来用比较柔和藕性低的方式帮助你解决这些问题.

Lifecycle


Lifecycle 是用来保存组件(例如 activity or a fragment) 生命周期状态的类,该类允许其他对象感知这个状态.

Lifecycle 在每个相关的组件中使用两个枚举类来追踪生命周期状态.

Event(事件)

由framework和 Lifecycle 类分发的生命周期事件.

State(状态)

Lifecycle 类持有的当前组件的状态.

image

按图思考一下图标上节点之间的状态和事件.

有个类可以通过添加注解的方法监控组件的生命周期状态.你可以为实现Lifecycle 接口的组件调用addObserver() 方法,来讲获得一个observer对象.示例代码如下.

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

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void disconnectListener() {
        ...
    }
}

myLifecycleOwner.getLifecycle().addObserver(new MyObserver());

在上述例子中,myLifecycleOwner 对象实现了 LifecycleOwner 接口,我们下面就来说说这个接口.

LifecycleOwner


LifecycleOwner 是一个只含有一个方法的接口,该类表示这个类含有一个Lifecycle .这个接口有一个叫做getLifecycle() 的方法.如果你想要管理生个APP的生命周期,那么看看ProcessLifecycleOwner.

这个接口把像FragmentAppCompatActivity 这样的类和生命周期之间进行抽象,允许针对这些类(fragment 和activity)书写一些组件.自定义的application都可以实现这个接口.

实现了 LifecycleObserver 接口的组件�可以和实现了LifecycleOwner 组件进行无缝连接,因为一个可以提供lifecycle,另一个可以观察lifecycle.

就拿地址追踪为例,我们可以让MyLocationListener实现LifecycleObserver 然后在onCreate() 方法中使用activity的 Lifecycle 进行初始化. 这样 MyLocationListener类便可以不要依赖activity实现完全的逻辑控制,达到自给自足.有了这些相对独立的组件,管理activity和fragment逻辑讲变得更简便.

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();
            }
        });
  }
}

一个常见的案例就是如果生命周期状态不稳定避免调用相关的回调. 例如当回调在activity 状态保存后运行fragment transaction,将导致crash(不了解的同学,复习一下fragment),有了这个接口之后再也不会再错误状态下调用这个回调了.

为了更好的使用,Lifecycle 类允许其他对象能够查询当前状态.

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.getCurrentState().isAtLeast(STARTED)) {
            // connect if not connected
        }
    }

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

有了这个实现,我们的LocationListener实现了完全的生命周期感知.如果我们想要在其他的activity或者fragment中使用它,我们只需要对其进行初始化即可.

如果一个library需要处理与生命周期相关的任务,那么我建议使用生命周期感知型组件.你的library能够轻易的管理生命周期,而不需要手工的写很多代码.

实现自定义的 LifecycleOwner

在Support Library 26.1.0以上的fragment和activity已经实现了LifecycleOwner 接口.

如果你又一个自定义类的,你想自己实现一个LifecycleOwner,你可以使用LifecycleRegistry类(LifecycleRegistry是lifecyle 类的子类,activity 和 fragment也是通过该类实现提供lifecycle对象),但是你需要将事件注入到该类里面,示例代码如下所示:

public class MyActivity extends Activity implements LifecycleOwner {
    private LifecycleRegistry mLifecycleRegistry;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mLifecycleRegistry = new LifecycleRegistry(this);
        mLifecycleRegistry.markState(Lifecycle.State.CREATED);
    }

    @Override
    public void onStart() {
        super.onStart();
        mLifecycleRegistry.markState(Lifecycle.State.STARTED);
    }

    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        return mLifecycleRegistry;
    }
}

生命周期感知型组件的最佳实践


  • 尽量保持你的UI控制器尽量精简,他们不应该自己去请求处理数据,应该使用ViewModel去处理数据,观察一个LiveData
    对象,并且将数据变化反应到试图上.

  • 尝试使用数据驱动型的UI,你的UI控制器相应数据变化,并且改变view ,或者通知user返回ViewModel

  • 将你的数据逻辑放入ViewModel
    ViewModel应该作为你的UI控制层和其他层的连接器,但是请狐疑获取网络数据不是viewmodel的职责.相反viewmodel 应该让组件去获取数据,然后处理结果返回给UI控制层.

  • 使用Data Binding 来在view和UI控制层直接维护一个简洁的接口.这可以允许你讲view更多的是声明式的,最小化更新代码.如果你想要这么做,可以使用Butter Knife 来获得更好的抽象.

  • If your UI is complex, consider creating a
    presenter
    class to handle UI modifications. This might be a laborious task, but it can make your UI components easier to test.

  • 如果你的UI界面相对复杂,考虑创建一个presenter 来处理UI 变化,这么做可能看起来工作量提升不少,不过这个让你的UI组将更容易测试.

  • ViewModel中避免使用view,activity,fragment的,这样可以避免内存泄露.

lifecycle-aware components使用场景


生命周期感知型的组件可以让管理生命变得更加简单,下面有几个例子:

  • 在高精度和低精度地理位置之间的切换.使用生命周期感知型的组件能够使用高精度获取地理位置在你的APP可见时候,当你的APP切换到后台的时候使用低精度更新.

  • 启动和关闭视频缓冲.使用生命周期感知型组件来打开视频缓冲.你也可以在在app关闭的时候关闭视频缓冲.

  • 启动和关闭网络连接.使用生命周期感知型组件能够允许app在前台时候更新网络数据,在app在后台时候自动暂停.

  • 暂停和重启动画,

处理 stop 事件


当一个lifecyle 属于AppCompatActivity或者Fragment 时,调用onSaveInstanceState(),方法, Lifecycle 状态变成 CREATED ,分发ON_STOP .

当fragment 或者AppCompatActivity状态在onSaveInstanceState() 保存了,他的界面就会被认为不可以改变,直到 ON_START 事件被调用. 在state被保存后修改UI,会因状态的不连续产生异常.这是为什么在app 状态被保存后运行FragmentTransaction 会报异常.

LiveData 避免了这些边缘性案例的产生,它通过观察相关的LifeCycle 状态是不是STARTED.在这个场景下它在调用observer之前调用了一个isAtLeast()

不幸的是,AppCompatActivityonStop( ) 方法在onSaveInstanceState() 之后被调用, 这会导致UI状态状态改变的时候留下一个空白期.

为了避免这个问题,Lifecycle 类在beta2 版本和更低的版本 标记状态为 CREATED ,这样任何代码都能检查当前状态,并且获得真是的值,知道onStop被系统调用.

不幸的是这个解决方案有两个主要问题:

  • 在Api 23以下,Android 系统保存状态即便activity被复写了.换句话说,Android系统调用onSaveInstanceState()但是不必调用onstop.这会产生一个潜在问题,有个时间段observer还认为生命周期处于活跃状态,但是事实UI状态不能被更改了.

  • 任何想要实现LiveData 类 的类似功能都要实现Lifecycle beta2 或以下的版本.

备注:

为了让整个流程更加简单,提供更好的兼容性,使用1.0.0-rc1 版本,

Lifecycle 对象会被标记成CREATED(状态)和ON_STOP(事件) 如果onSaveInstanceState() 被调用时,而不需要等到onStop()方法.这一般对你的代码不会造成什么影响,不过你还是应该知道在 API 26 及以下顺序和你想象的有些区别.

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

推荐阅读更多精彩内容