Android Architecture Component -- Lifecycle 浅析

Lifecycle

LifecycleAndroid Architecture Components 的一个组件,用于将系统组件(Activity、Fragment等等)的生命周期分离到 Lifecycle 类,Lifecycle 允许其他类作为观察者,观察组件生命周期的变化。Lifecycle 用起来很简单,首先声明一个 LifecycleObserver 对象,用 @OnLifecycleEvent 注解声明生命周期事件回调的方法:

public class LifecycleObserverDemo implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
    void onAny(LifecycleOwner owner, Lifecycle.Event event) {
        System.out.println("onAny:" + event.name());
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    void onCreate() {
        System.out.println("onCreate");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    void onDestroy() {
        System.out.println("onDestroy");
    }
}

然后在 LifecycleRegistryOwner 比如 LifecycleActivity 加入这么一行代码:

    getLifecycle().addObserver(new LifecycleObserverDemo());

然后?然后就没了,运行起来可以看到 LifecycleActivity 的生命周期发生变化时,LifecycleObserverDemo 总能得到通知。而 LifecycleActivity 只有寥寥几行代码,并没有覆盖任何回调方法。那么 Lifecycle 是怎么做到的,是不是有点黑魔法的感觉?

注解的作用

首先从注解入手,可以在 build 目录下发现注解处理器为我们生成了 LifecycleObserverDemo_LifecycleAdapter,不过这只是一个适配器,用于将生命周期事件派发到 LifecycleObserverDemo 对应的方法。

public class LifecycleObserverDemo_LifecycleAdapter implements GenericLifecycleObserver {
  final LifecycleObserverDemo mReceiver;

  LifecycleObserverDemo_LifecycleAdapter(LifecycleObserverDemo receiver) {
    this.mReceiver = receiver;
  }

  @Override
  public void onStateChanged(LifecycleOwner owner, Lifecycle.Event event) {
    mReceiver.onAny(owner,event);
    if (event == Lifecycle.Event.ON_CREATE) {
      mReceiver.onCreate();
    }
    if (event == Lifecycle.Event.ON_START) {
      mReceiver.onStart();
    }
    if (event == Lifecycle.Event.ON_PAUSE) {
      mReceiver.onPause();
    }
    if (event == Lifecycle.Event.ON_DESTROY) {
      mReceiver.onDestroy();
    }
  }

  public Object getReceiver() {
    return mReceiver;
  }
}

如何传达 lifecycle 事件

注解也没有生成任何相关的代码,而 Activity 不用写任何代码,那么 Lifecycle 是如何把 Activity 生命周期事件传递给 LifecycleObserver的?

最终通过研读 Lifecycle 的代码,发现里面有个包可见的类 LifecycleDispatcherLifecycleDispatcher 是一个单例,在 LifecycleDispatcher#init(Context) 中,它通过 registerActivityLifecycleCallbacks 方法,向当前的 Application 注册一个 DispatcherActivityCallback,但 Lifecycle 并没使用 ActivityLifecycleCallbacks 来监听并派发生命周期事件。

static void init(Context context){
    ...
    ((Application)context.getApplicationContext()).registerActivityLifecycleCallbacks(new LifecycleDispatcher.DispatcherActivityCallback());
    ...
}

static class DispatcherActivityCallback extends EmptyActivityLifecycleCallbacks {
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        ...
        if(manager.findFragmentByTag("android.arch.lifecycle.LifecycleDispatcher.report_fragment_tag") == null) {
            manager.beginTransaction().add(new ReportFragment(), "android.arch.lifecycle.LifecycleDispatcher.report_fragment_tag").commit();
            manager.executePendingTransactions();
        }
    }
}

而是通过一个无 UI 的 Fragment,在 DispatcherActivityCallback#onActivityCreated 可以看到它在 Activity#onCreate 时,为 Activity 添加一个 ReportFragment。最终由 ReportFragment 来监听各个生命周期事件,然后传递给 LifecycleRegistry

public class ReportFragment extends Fragment {
    ...
    public void onPause() {
        super.onPause();
        dispatch(Event.ON_PAUSE);
    }
    ...
    private void dispatch(Event event) {
        if(this.getActivity() instanceof LifecycleRegistryOwner) {
            ((LifecycleRegistryOwner)this.getActivity()).getLifecycle().handleLifecycleEvent(event);
        }
    }
}

Activity 的生命周期事件都会派发到它的 Fragments,向 Activity 注册一个无 UI 的 Fragment 也叫 Headless Fragment 用于将各种 Activity 回调分离出来是个常用的做法,比如 RxPermissions 也是用这种方法来避免复写 Activity#onRequestPermissionsResult

顺便一提 Lifecycle 文档提到:

ON_CREATE, ON_START, ON_RESUME events in this class are dispatched after the LifecycleOwner's related method returns. ON_PAUSE, ON_STOP, ON_DESTROY events in this class are dispatched before the LifecycleOwner's related method is called.

正好是 Fragment 生命周期回调的触发顺序。

Activity 的生命周期变化是如何传递到 LifecycleObserver 有了清晰的图表:

生命周期传递到 LifecycleObserver

LifecycleRuntimeTrojanProvider

还有一个问题, LifecycleDispatcher#init(Context) 并不是入口,它也需要被调用。那么他的调用者是谁?Google 这里的做法还是很巧妙的,如果这时把 apk 的 AndroidManifest.xml 提取出来,就会发现多了一个 ContentProvider 声明:

<provider
        android:name="android.arch.lifecycle.LifecycleRuntimeTrojanProvider"
        android:authorities="${applicationId}.lifecycle-trojan"
        android:exported="false"
        android:multiprocess="true" />

LifecycleRuntimeTrojanProvider,运行时木马是什么鬼?实际上,它不是一个 ContentProvider,而是利用 ContentProvider 的特点在应用程序初始化时,向其注入两行代码:

LifecycleDispatcher.init(getContext());
ProcessLifecycleOwner.init(getContext());

这里 ContentProvider 之于 Application 的作用就类似于 Headless Fragment 之于 Activity 一样,目的都是避免继承系统组件。关于 ContentProvider 的生命周期可以看 android - ContentProvider destruction/lifecycle - Stack Overflow

其他 LifecycleOnwer

最后再提一下,Lifecycle 还提供了内置了另外三个 LifecycleOnwer:

  1. LifecycleFragment
  2. LifecycleService,ServiceLifecycleDispatcher 将事件派发重新推到主线程消息队列,用于保证确保回调在 Service 生命周期回调后再调用。
  3. ProcessLifecycleOwner,用于监听整个应用的前后台切换。也是利用 ActivityLifecycleCallback 监听每个 Activity 的生命周期,如果 onStop 事件后,没有监听到任意的 onStart 事件,那么 ProcessLifecycleOwner 就会认为整个应用切换到后台,同时留下一个标志。如果监听到 onStart 事件,同时检查有标志那么就会认为应用回到前台。

Lifecycle 的应用?

有朋友在问 Lifecycle 有什么应用。我觉得 Lifecycle 最主要的作用就是在于解耦。以前我们使用一个生命周期敏感的模块 m,必须得在 Activity 子类里面添加类似下面的代码

super.onCreate()
m.init()

m.release()
super.onDestory()

这类组件之多,用起来之频繁。以致于我们经常要创建一个 BaseActivity 类来做这些脏活。不过,一旦我们建立了BaseActivity,我们常常就能体会到 Java 单继承之痛。Activity不止一个啊:

  • LifecycleActivity
  • AppcompatActivity
  • FragmentActivity
  • 等等…

还有第三方库的,比如 CordovaActivity… 。随着项目的扩大,你很难只用一类 Activity。而且有生命周期的组件不止一个,这些组件的子类也花样繁多,我们建立了 BaseFragmentBaseService…同时也建立了更多痛苦。为什么我们的模块要和这些复杂性绑定在一起?生命周期敏感模块应该与独立起来,变成一个可以在任意有生命周期的组件安装的模块,所以 Lifecycle 就在帮我们做这种事情。

getLifecycle().addObserver(new LifecycleObserverDemo());

那么具体一点这类生命周期敏感的组件有哪些呢?官方以 LocationManager 为例,主要作用避免 Activity 遁入后台后继续定位浪费电量。这里我以 volley 为例,举一个网络请求经常要面对的问题:

volleyClient.query(new Respose.Listener(){});

上面的代码是个老生常谈的问题了,new Respose.Listener(){} 是 Activity 的一个匿名类,它有指向 Activity 的引用,而 Volley 是一个存活范围比 Activity 更大的实例,比如常常 VolleyClient 就是单例。这就导致了 Activity 销毁后不能及时释放,内存泄漏!当然,网络请求终会返回的,这个回调对象就会被销毁,从这个角度讲,问题也不是很大。另外一个就是请求返回的时候,我们会在 onSuccess 里操作 UI,如果 Activity 已经销毁了,没做检查的话那么就会崩溃。这些都不是大问题,但是很烦人。所以像 volley 请求这样的就是一个生命周期敏感的功能。

网络请求和定位回调一样都可以归类为生命周期敏感的数据源,Google 为这种类型的数据源提供了 LiveData。这就是 LifecycleObserver 一个典型的应用,当然这只是 LiveData 的一部分功能。

非数据源的生命周期敏感组件,比如说用户行为收集模块,它本身就是一个生命周期的监听者,在没有 ActivityLifecycleCallback 的年代(API<14),常常需要在各个 Activity 中手动加入开始记录和停止纪录的代码。有了 ActivityLifecycleCallback 之后,我们需要做的就变成在 Application#onCreate 加一句代码。那么现在把用户行为收集模块变成 LifecycleObserver 有什么好处?

很遗憾,对于这个例子我暂时还想不出有什么特别好的地方,但是它能说明 LifecycleObserver 一个最主要的特点。比如我们有十个 Activity,只有 Activity1 和 Activity2 需要记录,那么用 LifecycleObserver 就可以避免用配置去声明哪些 Activity 需要记录。直接在需要记录的 Activity加入如下代码

getLifecycle().addObserver(new OpRecorder());

这就是解耦的好处。能让一个与生命周期深度耦合的组件变成一个随处可安装的组件。

最后,还是要回到这篇文章的主题,我们从 Lifecycle 的代码可以学到一个更大的模式。

Activity 不只有生命周期回调,还有权限,onActivityResult 等等。那些需要与这些回调深度耦合的模块,利用 Lifecycle 用的技术 Headless Fragments 来解耦是个不错的方法。对于整个 Application 来说那就可以用更 tricky 的 Headless ContentProvider。

原文链接:https://dourok.info/2017/05/23/lifecycle/

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

推荐阅读更多精彩内容