Lifecycle——Google组件开发

2017年Google I/O大会不仅发布了O预览版,同时还将kotlin作为安卓开发的新宠儿。今天要介绍的是Google推出的应用架构组件。
  众所周知,安卓开发架构一直很自由,从MVC到MVP、MVVM等,今年I/O大会上Google也推出了一份关于应用架构的最佳实践指南。
  Android框架具有明确的API来处理与操作系统的联系点,例如Activity,但这些是进入应用程序的入口点,而不是为应用程序架构构建块,框架组件不会强制您将数据模型与UI组件分开,或提供一种清晰的方式来保持与生命周期无关的数据。

Google官方架构

  • 不要在应用程序组件中保存任何应用数据或状态,并且组件间也不应该相互依赖
  • 通过 model 驱动应用 UI,并尽可能的持久化

官方架构构建演示

Android Framework中定义的大多数应用程序组件都附有生命周期,这些生命周期由运行的操作系统或框架代码进行管理。它们是Android如何工作的核心,应用程序必须严格遵循它们。不这样做可能会触发内存泄漏甚至应用程序崩溃。
举个栗子:

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

上面的代码看起来就像我们平常的代码风格,但在一个真实的应用程序中,可能会遇到大量相似的调用,从而使onStart()和onStop()方法变得非常臃肿。另外如果一些组件不能在onStart()中启动,我们需要在启动位置观察器之前检查一些配置怎么办?在某些情况下,可以在Activity停止后检查完成,这意味着myLocationListener.start()将被调用myLocationListener.stop()之后,基本上保持连接。

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

Lifecycles 提供了一系列方法,可帮助您以弹性和孤立的方式解决这些问题。

Lifecycles

LIfecycle是一个保存有关组件生命周期状态的信息,并允许其他对象观察此状态的类,将系统组件(Activity、Fragment 等等)的生命周期分离到 Lifecycle 类,Lifecycle 类允许其他类作为观察者,在外部观察系统组件生命周期的变化。它使用两个主要枚举来跟踪其关联组件的生命周期状态。

  • Event:这些是从框架和Lifecycle类调度的生命周期事件。这些事件映射到Activity和Fragment中的回调事件。
  • State:由Lifecycle对象跟踪的组件的当前状态

首先通过向其方法添加注解@OnLifecycleEvent来监视组件的生命周期状态

public class MyObserver implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
    public void onAny(LifecycleOwner owner, Lifecycle.Event event) {
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void onResume() {
    }

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

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onDestroy() {
    }
}
aLifecycleOwner.getLifecycle().addObserver(new MyObserver());

然后在 LifecycleRegistryOwner 比如 LifecycleActivity 加入上面最后一行代码

在Activity和Fragment中实现LifecycleOwner。通过实现内置的LifecycleRegistryOwner接口(而不是扩展LifecycleFragment或LifecycleActivity),任何Activity和Fragment都可以转换为LifecycleOwner

public class MyFragment extends Fragment implements LifecycleRegistryOwner {
    LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);

    @Override
    public LifecycleRegistry getLifecycle() {
        return lifecycleRegistry;
    }
}

如果您有一个要创建LifecycleOwner的自定义类,则可以使用LifecycleRegistry类,但是您需要将事件转发到该类中。如果片段和活动实现了LifecycleRegistryOwner接口,则此转发将自动完成。

如果观察者指定LifeCycle处于Started或者RESUMED状态,LiveData会将观察者视为活动状态,并通知其数据的变化。
有关LiveData会在后面介绍。

LifecycleOwner

LifeCycleOwner是一个只有一个方法的接口用于表明其有一个LifeCycle对象。这个方法为getLifecycle(),它必须由类实现。
  LifecycleOwner从各个类(如Activity和Fragment)抽象生命周期的所有权,并允许编写可与两者兼容的组件。Activity和Fragment只要实现这个接口就能配合LifeCycle实现生命周期监听。任何自定义应用程序类都可以实现LifecycleOwner接口。

注意 : 由于Lifecycles项目处于alpha阶段,因此Fragment和AppCompatActivity类无法实现(因为我们无法将稳定组件的依赖关系添加到不稳定的API)。在Lifecycles稳定之前,为了方便起见,提供了LifecycleActivity和LifecycleFragment类。Lifecycles项目发布后,支持库片段和活动将实现LifecycleOwner接口;当时将不推荐使用LifecycleActivity和LifecycleFragment。

对于上面的示例,我们可以使MyLocationListener类成为LifecycleObserver,并使用onCreate上的生命周期进行初始化,并在此之后不再担心。这允许MyLocationListener类自足,这意味着它可以在必要时进行自己的清理。

class MyActivity extends LifecycleActivity {
    private MyLocationListener myLocationListener;

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

为了简化此用例,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.getState().isAtLeast(STARTED)) {
            // connect if not connected
        }
    }

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

通过这个实现,我们的LocationListener类是完全生命周期感知的;它可以进行自己的初始化和清理而不受Activity或者Fragment的管理。如果我们需要从另一个Activity或另一个Fragment使用我们的LocationListener,我们需要做的就是初始化它。所有的安装和拆卸操作都将由类本身管理。

可以与Lifecycle一起使用的类称为生命周期感知组件。鼓励提供需要使用Android生命周期的类的库可以提供生命周期感知组件,以便客户端可以轻松地在客户端集成这些类,而无需手动生命周期管理。

LiveData是生命周期感知组件的示例。与ViewModel一起使用LiveData可以在遵循Android生命周期的情况下,更容易地使用数据填充UI

Lifecycle实践准则

  • 保持UI控制器(Activity和Fragment)尽可能体积小。他们不应该试图获取他们的数据;而是使用ViewModel来执行此操作,并观察LiveData以将更改反映到视图中
  • 尝试编写数据驱动的UI,您的UI控制器的责任是在数据更改时更新视图,或将用户操作通知给ViewModel
  • 将您的数据逻辑放在ViewModel类中。 ViewModel应该作为UI控制器和其他应用程序之间的连接器。请注意,ViewModel不是提取数据(例如,从网络)的责任。相反,ViewModel应该调用相应的组件来执行此工作,然后将结果提供给UI Controller。
  • 使用数据绑定在您的视图和UI控制器之间具有干净的界面。这将允许您使您的视图更具声明性,并最大限度地减少您需要在活动和片段中写入的更新代码。如果您更喜欢在Java中执行此操作,请使用像Butter Knife这样的库来避免使用样板代码并进行更好的抽象。
  • 如果您有一个复杂的UI,请考虑创建一个Presenter类来处理UI修改。这通常是过度的,但可能有助于使您的UI更容易测试。
  • 不要在ViewModel中引用View或Activity上下文。如果ViewModel超过活动(在配置更改的情况下),您的活动将被泄露,并且不正确地垃圾回收
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355

推荐阅读更多精彩内容