jetpack系列再看lifecycle实现原理细节

在21年时候有写过一次lifecycle: http://08643.cn/p/838631cdf520。时隔两三年了刚好最近在看jetpack相关的一些库?;毓讼耹ifecycle实现源码. 现在再来看感受又不一样,之前可能注重他的流程。现在更多关注他的实现细节和为什么这么设计,这样设计的好处是什么,可能多些自己的思考吧。

1.总体的流程

lifecycle 整个实现基于观察者模式,比如我们activity就是被观察者,我们自己定义一个观察者 通过addObserver方法 传入观察者,把观察者对象保存在缓存Map中,而在componentActivty中 关联了Actiivty生命周期(29以上Activity LifecycleCallbacks生命周期回调或者空fragment实现。 内部维护了5个状态和6个事件,来保证观察者 被观察者的状态一致,如果不一致会触发执行sync方法从当前被观察者的状态获取事件,观察者分发调用事件,触发onstateChange方法反射调用观察者不同的注解生命周期方法。

2.源码实现

大部分源码在上一篇已经有了,这里主要补充一下新版本特性和一些细节

2.1.被观察者

Lifecycle是一个接口,里面定义了addObsever()、removeObserver、两个枚举State、Event以及根据Event获取State、根据State获取Event的方法 整个框架要用的都定义在里面。实现类是LifecycleRegistry

1.androidx的默认Activity继承了ComponentActivity,而ComponentActivity实现了LifeCycleOwner接口

public class ComponentActivity extends Activity implements
        LifecycleOwner,

LifecycleOwner接口里面只提供了getLifecycle方法

public interface LifecycleOwner {
    @NonNull
    Lifecycle getLifecycle();
}

也正是因为提供了这个方法,他的实现类可以直接调用这个方法,看componentActivity的实现是:

private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
return mLifecycleRegistry;

Fragment 类似的也实现了LifecycleOwner接口:

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

完成基本介绍后 我们直接看重点ComponentActivity中的onCreate方法:

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ReportFragment.injectIfNeededIn(this);
    }

ReportFragment目的就是参考Glide框架添加一个没有UI的fragment到activity上,通过FragmentManager关联,在Activity源码中也能看到生命周期变化会调用FragmentController去dispatch对应fragment生命周期函数.

看到添加方法实现如下,判断版本是否大于29:

 public static void injectIfNeededIn(Activity activity) {
        if (Build.VERSION.SDK_INT >= 29) {
            // On API 29+, we can register for the correct Lifecycle callbacks directly
            LifecycleCallbacks.registerIn(activity);
        }
        // Prior to API 29 and to maintain compatibility with older versions of
        // ProcessLifecycleOwner (which may not be updated when lifecycle-runtime is updated and
        // need to support activities that don't extend from FragmentActivity from support lib),
        // use a framework fragment to get the correct timing of Lifecycle events
        android.app.FragmentManager manager = activity.getFragmentManager();
        if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
            manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
            // Hopefully, we are the first to make a transaction.
            manager.executePendingTransactions();
        }
    }

根据版本判断api29以上注册ActivityLifecycleCallbacks回调监听生命周期,以下的版本添加fragment关联生命周期。主要为了兼容高低版本生命周期变化。
`
最终都会调用 dispatch(activity, Lifecycle.Event.ON_XXX);区别是29以上在回调里面调用,29以下是在ReportFragment生命周期方法中调用

    static void dispatch(@NonNull Activity activity, @NonNull Lifecycle.Event event) {
        if (activity instanceof LifecycleRegistryOwner) {
            ((LifecycleRegistryOwner) activity).getLifecycle().handleLifecycleEvent(event);
            return;
        }

        if (activity instanceof LifecycleOwner) {
            Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
            if (lifecycle instanceof LifecycleRegistry) {
                ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
            }
        }
    }

核心代码调用handleLifecycleEvent 里面又调用

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

这里需要注意的是第一个mState == next 判断,也就是观察者lifecycle的状态和被观察者activit的状态如果是一样的,直接不执行后续逻辑,也就不会触发观察者的生命周期方法,
主要还是保证观察者被观察者状态一致,如果不一致则对mstate赋值,再调用sync方法

 private void sync() {
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
            throw new IllegalStateException("LifecycleOwner of this LifecycleRegistry is already"
                    + "garbage collected. It is too late to change lifecycle state.");
        }
        while (!isSynced()) {
            mNewEventOccurred = false;
            // no need to check eldest for nullability, because isSynced does it for us.
            if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
                backwardPass(lifecycleOwner);
            }
            Map.Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
            if (!mNewEventOccurred && newest != null
                    && mState.compareTo(newest.getValue().mState) > 0) {
                forwardPass(lifecycleOwner);
            }
        }
        mNewEventOccurred = false;
    }

里面代码也比较清晰,如果观察者被观察者状态不一致一致执行循环保证一致,不一致则比较谁的状态值大,分别状态迁移 后移。

 Event event = Event.downFrom(observer.mState);
                if (event == null) {
                    throw new IllegalStateException("no event down from " + observer.mState);
                }
                pushParentState(event.getTargetState());
                observer.dispatchEvent(lifecycleOwner, event);

里面其实是根据当前的状态,取出事件类型再调用observer的分发事件 来保持状态一致。而分发事件,核心代码是下面的:

  mLifecycleObserver.onStateChanged(owner, event);

onStateChanged接口方法的实现是初始化时候addObsever()时候保存的观察者信息类到
ReflectiveGenericLifecycleObserver

 void invokeCallbacks(LifecycleOwner source, Lifecycle.Event event, Object target) {
            invokeMethodsForEvent(mEventToHandlers.get(event), source, event, target);
            invokeMethodsForEvent(mEventToHandlers.get(Lifecycle.Event.ON_ANY), source, event,
                    target);
        }

里面用到线程安全的map,key是观察者,value是包装后的对象里面保存了观察者和初始状态
再通过反射调用观察者的对应注解的方法

3.思考和总结

1.为什么涉及mActive变量?
格局出发,mActive设计不知给自己用,还为了给别的框架用。lifecycle虽然是一个组件,更多的是他结合liveData viewModle等一起用,比如liveData在mActive为onStart、onResume才执行一些逻辑 否则直接过滤,保证了一些场景安全,页面都关了网络请求回来刷新数据。
2.为什么要把状态涉及为复用的?
个人猜测是为了减少状态的值少两个,同时区分开Activity的本身状态。如果定义了和Activit生命周期一样的状态,感觉会比较重复,但是又为了记录当前状态。

3.里面设计思想?设计模式?
源码里面真的是大量使用设计模式,这里用到观察者模式、建造者模式、享元、装饰、模版方法、工厂、单例很多都涉及到了。不一一举例。

4.ReportFragment的injectIfNeededIn方法为什么要区分29以上版本
API 29 或以上版本进行区分的原因主要在于 Android 对 Fragment 的管理和生命周期有所变化
以前的版本中,ReportFragment 需要用老的 android.app.Fragment 来保证与老的 lifecycle-runtime 的兼容性。
从 API 29 开始,Android 引入了新的 Fragment API(androidx.fragment.app.Fragment),并且对 Fragment 生命周期进行了优化,当 Activity 调用 onStop 时,Fragment 的 onStop 会先于 Activity 的 onStop 被调用,这样就可以在 Fragment 中更早地保存状态。而在 API 29 之前,Fragment 的 onStop 是在 Activity 的 onStop 之后调用的。
5.使用场景如何,用起来是否方便
一般结合LiveData viewModle使用,上家公司也直接结合MVP模式管理生命周期防止业务开发同学忘记手动释放资源。

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

推荐阅读更多精彩内容