Architecture Components 知识梳理(1) - Lifecycle 示例&原理

一、概述

听说这一套ToolKit已经有很长一段时间了,一直没有系统的学习过,前几天有同事在项目中通过Lifecycles解决了监听Activity生命周期的问题,不得不说,真的是一套强大的框架,除此之外,前段时间Google还更新了NavigationPagingWorkManager用来解决 页面的管理 以及 任务的调度 问题,更激发了我学习的动力。

依照惯例,先从最基本的Demo学起,然后再去分析源码。

二、导入依赖

根目录build.gradle文件中,加上google()的远程仓库声明(现在AS创建的工程已经默认包含了该声明)。

allprojects {
    repositories {
        jcenter()
        //加上这一句。
        google()
    }
}

当我们使用了'com.android.support:appcompat-v7:27.1.1'时,其实已经默认导入了lifecycles相关的库,由此可见,这确实是之后的一个趋势。

默认导入的依赖

如果我们没有使用support:appcompat,那么就需要自己去声明依赖,当然并不是说要导入全部的依赖,而是可以根据需要去选择对应的依赖库,具体的说明在注释中。

  • 选择仅导入LiveDataViewModelLifecycles的其中一种,或者全部导入。
  • 注解处理器,在Java8上使用common-java8,在Java8以下使用compiler
  • 流式处理支持库reactivestreams
  • 测试支持库core-testing
dependencies {
    def lifecycle_version = "1.1.1"

    // ViewModel and LiveData
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
    // alternatively - just ViewModel
    implementation "android.arch.lifecycle:viewmodel:$lifecycle_version" // use -ktx for Kotlin
    // alternatively - just LiveData
    implementation "android.arch.lifecycle:livedata:$lifecycle_version"
    // alternatively - Lifecycles only (no ViewModel or LiveData), Support library depends on this lightweight import
    implementation "android.arch.lifecycle:runtime:$lifecycle_version"

    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version"
    // alternately - if using Java8, use the following instead of compiler
    implementation "android.arch.lifecycle:common-java8:$lifecycle_version"

    // optional - ReactiveStreams support for LiveData
    implementation "android.arch.lifecycle:reactivestreams:$lifecycle_version"

    // optional - Test helpers for LiveData
    testImplementation "android.arch.core:core-testing:$lifecycle_version"
}

这里,我们直接使用appcompat当中导入的组件即可。

三、Lifecycle

3.1 解决的问题

Android开发当中,生命周期 是一个很重要的东西,因为我们往往需要在页面创建onCreate、页面展示onResume,和页面销毁onDestroy的时候,去进行业务逻辑的处理,而在此之前,我们只能通过ActivityFragment才能收到生命周期的回调,而其它的组件,例如Fragment当中的View需要收到生命周期的回调,那么只能通过ActivityFragment去通知它,这就造成了很多冗余的代码,特别是当我们期望提供一个View作为SDK给接入方时,又无形中增加了接入方的成本。

如果任何一个对象,通过观察者的方式,能够收到Activity/Fragment生命周期的各种回调就好了。没错!Lifecycle就是这么一个东西!

3.2 示例

  • 首先,我们定义一个类LiveObserver,该类 实现LifecycleObserver接口,它定义了一系列的方法并用@OnLifecycleEvent(Lifecycle.Event.xxx)进行修饰,Lifecycle.Event的可选类型有如下几种,很明显可以看出是生命周期相关的东西。
    public enum Event {
        ON_CREATE,
        ON_START,
        ON_RESUME,
        ON_PAUSE,
        ON_STOP,
        ON_DESTROY,
        ON_ANY
    }

在这些方法中添加Log用于调试:

public class LiveObserver implements LifecycleObserver {

    private static final String TAG = LiveObserver.class.getSimpleName();

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    void onCreate() {
        Log.d(TAG, "onCreate()");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void onStart() {
        Log.d(TAG, "onStart()");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    void onResume() {
        Log.d(TAG, "onResume()");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    void onPause() {
        Log.d(TAG, "onPause()");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void onStop() {
        Log.d(TAG, "onStop()");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    void onDestroy() {
        Log.d(TAG, "onDestroy()");
    }
}
  • 第二步,编写测试的Demo,注意,这里我们是通过AppCompatActivity提供的getLifecycle#addObserver方法来实现对第一步中的LiveObserver实例进行监听的:
public class LiveAppCompactActivity extends AppCompatActivity {

    private Button mBtnBind;
    private Button mBtnUnBind;
    private LifecycleObserver mObserver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_live_appcompact);
        mBtnBind = findViewById(R.id.bt_bind);
        mBtnBind.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if (mObserver == null) {
                    mObserver = new LiveObserver();
                    getLifecycle().addObserver(mObserver);
                }
            }
        });
        mBtnUnBind = findViewById(R.id.bt_unbind);
        mBtnUnBind.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if (mObserver != null) {
                    getLifecycle().removeObserver(mObserver);
                    mObserver = null;
                }
            }
        });

    }
}

3.3 模拟场景

场景一:点击绑定按钮

当界面完全可见的时候,我们点击“绑定”按钮,Log输出为:

点击绑定按钮后输出

场景二:点击绑定按钮,点击解绑按钮

没有任何输出。

场景三:再次点击绑定按钮

再次点击绑定按钮

场景四:在绑定状态下,锁屏或者按 Home 回到桌面

锁屏或按 Home 回到说面

场景五:解锁或者点击图标重新进入

解锁或者点击图标重新进入

场景六:按 Back 回到桌面

按 Back 回到桌面

场景结论

  • 第一次绑定 的时候,LifeObserver会从onCreate()方法依次回调到Activity当前所处的生命周期状态。
  • 解绑 不会触发任何生命周期回调,之后也收不到Activity的任何通知。
  • 在绑定过后,LifeObserver会跟随着Activity的生命周期变化。

3.4 不使用 AppCompactActivity 实现

假如我们使用的不是AppCompactActivity,那么要怎么达到和3.2中一样的效果呢,实现方式如下:

  • 让普通的Activity实现LifecycleOwner接口
  • 在其内部创建一个LifecycleRegistry对象,其构造函数参数为实现了LifecycleOwner接口的ActivitygetLifecycle()方法返回该对象。
  • 通过LifecycleRegistry#addObserver注册观察者LiveObserver

整体的实现代码如下所示:

/**
 * 1.需要让 Activity 实现 LifecycleOwner 接口。
 */
public class LiveActivity extends Activity implements LifecycleOwner {

    private Button mBtnBind;
    private Button mBtnUnBind;
    //2.创建 mLifecycleRegistry 对象,其构造函数为实现了 LifecycleOwner 的对象。
    private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
    private LifecycleObserver mObserver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_live);
        mBtnBind = findViewById(R.id.bt_bind);
        mBtnBind.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if (mObserver == null) {
                    mObserver = new LiveObserver();
                    //4.1 注册的方法和之前相同。
                    getLifecycle().addObserver(mObserver);
                }
            }
        });
        mBtnUnBind = findViewById(R.id.bt_unbind);
        mBtnUnBind.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if (mObserver != null) {
                    //4.2 反注册的方法和之前相同。
                    getLifecycle().removeObserver(mObserver);
                    mObserver = null;
                }
            }
        });
    }

    /**
     * 3.该函数返回 mLifecycleRegistry。
     *
     * @return 返回 mLifecycleRegistry。
     */
    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        return mLifecycleRegistry;
    }
}

其效果和之前3.3中的表现是一致的。

四、实现原理

有没有感到很神奇,我们是怎么让一个独立的对象和Activity/Fragment的生命周期关联起来的呢。通过源码我们可以发现,有以下两个类实现了LifecycleOwner接口:

  • android.support.v4.app.SupportActivity
@RestrictTo(LIBRARY_GROUP)
public class SupportActivity extends Activity implements LifecycleOwner {

    private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);

    @Override
    public Lifecycle getLifecycle() {
        return mLifecycleRegistry;
    }
   
}
  • android.support.v4.app.Fragment
public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
        ViewModelStoreOwner {

    LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);

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

当我们分析完之后,会发现这两种使用的是不同的方式来实现我们最终看到的效果的。

4.1 使用 Activity 的 getLifecycle 注册

当我们使用SupportActivitygetLifecycle(),其实系统在Activity启动的时候 偷偷 地为我们添加了一个没有界面的ReportFragment

@RestrictTo(LIBRARY_GROUP)
public class SupportActivity extends Activity implements LifecycleOwner {
    
    @Override
    @SuppressWarnings("RestrictedApi")
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //在 onCreate 方法当中偷偷地添加。
        ReportFragment.injectIfNeededIn(this);
    }

}

用过Fragment的同学们都知道,Fragment的生命周期是和它所在的Activity绑定的,那么当Activity的状态变化时,Fragment的生命周期就会被回调,我们来看一下ReportFragment是怎么处理的:

public class ReportFragment extends Fragment {

    @Override
    public void onResume() {
        super.onResume();
        //这里会去分发事件。
        dispatch(Lifecycle.Event.ON_RESUME);
    }

    private void dispatch(Lifecycle.Event event) {
        Activity activity = getActivity();
        //这里的 activity 就是我们上面的 SupportActivity,而 getLifecycle 就返回了 LifecycleRegistry 对象。
        if (activity instanceof LifecycleOwner) {
            Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
            if (lifecycle instanceof LifecycleRegistry) {
                ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
            }
        }
    }
}

接着,就会走到LifecycleRegistryhandleLifecycleEvent方法当中,具体的逻辑如下所示,最终会通知到它内部所持有的LifecycleObserver

public class LifecycleRegistry extends Lifecycle {
    public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
       //获取下一步的状态。
       State next = getStateAfter(event);
        //转移到该状态。
        moveToState(next);
    }

    private void moveToState(State next) {
        if (mState == next) {
            return;
        }
        mState = next;
        if (mHandlingEvent || mAddingObserverCounter != 0) {
            mNewEventOccurred = true;
            // we will figure out what to do on upper level.
            return;
        }
        mHandlingEvent = true;
        //状态同步。
        sync();
        mHandlingEvent = false;
    }

    private void sync() {
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
            Log.w(LOG_TAG, "LifecycleOwner is garbage collected, you shouldn't try dispatch "
                    + "new events from it.");
            return;
        }
        while (!isSynced()) {
            mNewEventOccurred = false;
            if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
                //1.向后转移。
                backwardPass(lifecycleOwner);
            }
            Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
            if (!mNewEventOccurred && newest != null
                    && mState.compareTo(newest.getValue().mState) > 0) {
                //2.向前转移
                forwardPass(lifecycleOwner);
            }
        }
        mNewEventOccurred = false;
    }

    private void forwardPass(LifecycleOwner lifecycleOwner) {
        Iterator<Entry<LifecycleObserver, ObserverWithState>> ascendingIterator =
                mObserverMap.iteratorWithAdditions();
        while (ascendingIterator.hasNext() && !mNewEventOccurred) {
            Entry<LifecycleObserver, ObserverWithState> entry = ascendingIterator.next();
            ObserverWithState observer = entry.getValue();
            while ((observer.mState.compareTo(mState) < 0 && !mNewEventOccurred
                    && mObserverMap.contains(entry.getKey()))) {
                pushParentState(observer.mState);
                //这里的 observer 就是我们通过 addObserver 加入的监听者,它会根据回调的状态和注解去调用对应的方法。
                observer.dispatchEvent(lifecycleOwner, upEvent(observer.mState));
                popParentState();
            }
        }
    }
}

整个状态的传递如下图所示:

状态传递

最后,让我们用断点验证一下之前的分析,以onResume为例:

onResume 方法的调用过程

4.2 使用 Fragment 的 getLifecycle 注册

下面我们再来分析Fragment的实现过程,Fragment的实现就简单多了,它是直接在对应的生命周期里,通过内部持有的LifecycleRegistry对象调用的,很好理解:

public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
        ViewModelStoreOwner {

    void performResume() {
        //简单粗暴,直接通过内部持有的对象调用。
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
    }
}

之后handleLifecycleEvent的过程就和4.1中一致了,就不过多分析了,大家只需要了解基本的思想就好了,不必过于深究源码。

五、使用 Lifecycle 应该注意什么

https://developer.android.google.cn/topic/libraries/architecture/lifecycle 中提到了下面两点,属于对lifecycle组件的总结,但是很多东西,我们还没有学到,因此先留下原文,等学习完后几章的内容后,再来理解。

  • Best practices for lifecycle-aware components
  • Use cases for lifecycle-aware components

5.1 Best practices for lifecycle-aware components

  • Keep your UI controllers (activities and fragments) as lean as possible. They should not try to acquire their own data; instead, use a ViewModel to do that, and observe a LiveData object to reflect the changes back to the views.
  • Try to write data-driven UIs where your UI controller’s responsibility is to update the views as data changes, or notify user actions back to the ViewModel.
  • Put your data logic in your ViewModel class. ViewModel should serve as the connector between your UI controller and the rest of your app. Be careful though, it isn't ViewModel's responsibility to fetch data (for example, from a network). Instead, ViewModel should call the appropriate component to fetch the data, then provide the result back to the UI controller.
  • Use Data Binding to maintain a clean interface between your views and the UI controller. This allows you to make your views more declarative and minimize the update code you need to write in your activities and fragments. If you prefer to do this in the Java programming language, use a library like Butter Knife to avoid boilerplate code and have a better abstraction.
  • 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.
  • Avoid referencing a View or Activity context in your ViewModel. If the ViewModel outlives the activity (in case of configuration changes), your activity leaks and isn't properly disposed by the garbage collector.

5.2 Use cases for lifecycle-aware components

  • Switching between coarse and fine-grained location updates. Use lifecycle-aware components to enable fine-grained location updates while your location app is visible and switch to coarse-grained updates when the app is in the background. LiveData, a lifecycle-aware component, allows your app to automatically update the UI when your user changes locations.
  • Stopping and starting video buffering. Use lifecycle-aware components to start video buffering as soon as possible, but defer playback until app is fully started. You can also use lifecycle-aware components to terminate buffering when your app is destroyed.
  • Starting and stopping network connectivity. Use lifecycle-aware components to enable live updating (streaming) of network data while an app is in the foreground and also to automatically pause when the app goes into the background.
  • Pausing and resuming animated drawables. Use lifecycle-aware components to handle pausing animated drawables when while app is in the background and resume drawables after the app is in the foreground.

六、参考文章

(1) Handling lifecycles with lifecycle-aware components
(2) Android 官方架构组件 Lifecycle 详解 & 原理分析
(3) Android 架构组件(一)——Lifecycle

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

推荐阅读更多精彩内容