携手创作,一起生长!这是我参与「日新方案 8 月更文挑战」的第6天,点击检查活动概况

本篇文章主要是介绍lifecycle-runtime-ktx的两个我们用的比较少的API:findViewTreeLifecycleOwnerwithCreated/Started/Resumed() 系列。

View.findViewTreeLifecycleOwner()

这个是ifecycle-runtime-ktx官方库提供的一个扩展办法简化获取LifecycleOwner的逻辑:

public fun View.findViewTreeLifecycleOwner(): LifecycleOwner? = ViewTreeLifecycleOwner.get(this)

最终调用:

public static LifecycleOwner get(@NonNull View view) {
    LifecycleOwner found = (LifecycleOwner) view.getTag(R.id.view_tree_lifecycle_owner);
    if (found != null) return found;
    ViewParent parent = view.getParent();
    while (found == null && parent instanceof View) {
        final View parentView = (View) parent;
        found = (LifecycleOwner) parentView.getTag(R.id.view_tree_lifecycle_owner);
        parent = parentView.getParent();
    }
    return found;
}

能够看到最终是遍历view树,从View的tag中经过R.id.view_tree_lifecycle_owner获取的:

@UnsupportedAppUsage
//键值为非装箱的基本数据类型int
private SparseArray<Object> mKeyedTags;
public Object getTag(int key) {
    if (mKeyedTags != null) return mKeyedTags.get(key);
    return null;
}

咱们看下这个tag是在哪里赋值的:

看下AppCompatActivitysetContentView()办法:

@Override
public void setContentView(@LayoutRes int layoutResID) {
    initViewTreeOwners();
    getDelegate().setContentView(layoutResID);
}

走进initViewTreeOwners()办法看下:

private void initViewTreeOwners() {
    ViewTreeLifecycleOwner.set(getWindow().getDecorView(), this);
    ...
}
//ViewTreeLifecycleOwner.java
public static void set(@NonNull View view, @Nullable LifecycleOwner lifecycleOwner) {
    view.setTag(R.id.view_tree_lifecycle_owner, lifecycleOwner);
}

能够看到,便是在这儿进行赋值的,其间这个参数view便是DecorView

LifecycleOwner.withCreated/Started/Resumed()

这儿咱们以LifecycleOwner.withStarted()举例,这个办法是带有返回值的且确保在主线程履行,在未到达指定履行生命周期且返回成果履行完毕之前,运转的协程会进行挂起:

public suspend inline fun <R> LifecycleOwner.withStarted(
    crossinline block: () -> R
): R = lifecycle.withStateAtLeastUnchecked(
    state = Lifecycle.State.STARTED,
    block = block
)

最终走到lifecycle.withStateAtLeastUnchecked()办法:

@PublishedApi
internal suspend inline fun <R> Lifecycle.withStateAtLeastUnchecked(
    state: Lifecycle.State,
    crossinline block: () -> R
): R {
    //1.指定主线程调度器,判别是否需求分发
    val lifecycleDispatcher = Dispatchers.Main.immediate
    val dispatchNeeded = lifecycleDispatcher.isDispatchNeeded(coroutineContext)
    //2.直接履行,无需分发
    if (!dispatchNeeded) {
        if (currentState == Lifecycle.State.DESTROYED) throw LifecycleDestroyedException()
        if (currentState >= state) return block()
    }
    //3.挂起履行
    return suspendWithStateAtLeastUnchecked(state, dispatchNeeded, lifecycleDispatcher) {
        block()
    }
}

接下来咱们来一步步进行剖析:

1.判别是否在主线程调度履行

Dispatchers.Main.immediate代表主线程调度器,经过isDispatchNeeded判别当时的履行环境是否处于主线程,假如是将履行履行协程代码块,否则需求调度器分发到主线程再进行履行。

2.主线程且大于等于STARTED直接进行履行

  • 主线程环境下,首要判别当时界面已经处于销毁状况,是直接抛出LifecycleDestroyedException反常,完毕;

  • 假如界面状况大于等于STARTED状况,才直接履行协程代码块,假如界面状况小于STARTED,就需求调度

3.非主线程或小于STARTED挂起协程

下面走进suspendWithStateAtLeastUnchecked()函数:

@PublishedApi
internal suspend fun <R> Lifecycle.suspendWithStateAtLeastUnchecked(
    state: Lifecycle.State,
    dispatchNeeded: Boolean,
    lifecycleDispatcher: CoroutineDispatcher,
    block: () -> R
): R = suspendCancellableCoroutine { co ->
    val observer = object : LifecycleEventObserver {
        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
            if (event == Lifecycle.Event.upTo(state)) {
                removeObserver(this)
                //2.到达履行`Started`状况康复挂起的协程
                co.resumeWith(runCatching(block))
            } else if (event == Lifecycle.Event.ON_DESTROY) {
                removeObserver(this)
                //3.到达`DESTROYED`状况抛出反常
                co.resumeWithException(LifecycleDestroyedException())
            }
        }
    }
    //1.添加观察者
    if (dispatchNeeded) {
        lifecycleDispatcher.dispatch(
            EmptyCoroutineContext,
            Runnable { addObserver(observer) }
        )
    } else addObserver(observer)
    //3.移除观察者
    co.invokeOnCancellation {
        if (lifecycleDispatcher.isDispatchNeeded(EmptyCoroutineContext)) {
            lifecycleDispatcher.dispatch(
                EmptyCoroutineContext,
                Runnable { removeObserver(observer) }
            )
        } else removeObserver(observer)
    }
}

首要说明下suspendWithStateAtLeastUnchecked()为什么运用@PublishedApi注解润饰,因为内联函数中调用的办法只能是public办法,而suspendWithStateAtLeastUnchecked()是个internal,所以需求增加该注解声明。

suspendCancellableCoroutine()办法捕捉Continuation并决议被挂起的协程的康复机遇。

  1. 监听界面状况必定需求添加观察者将协程与界面生命周期绑定,所以这步便是添加观察者

  2. 观察者主要干了两件事情:

  • 收到界面销毁ON_DESTROY,抛出反常LifecycleDestroyedException,并康复挂起协程的履行;

  • 到达指定的Started状况,直接履行运用runCatching(捕捉反常)包裹的协程代码块,拿到成果后调用resumeWith康复被挂起的协程;

  1. 协程被撤销则撤销观察者注册

这个便是为了兜底,一般都是建议运用Activity的lifecycleScopeviewModelviewModelScope作为协程效果域,因为这两个是和对应组件的生命周期绑定的,这样当组件销毁/清楚,该效果域下的子协程就会被撤销,咱们经过invokeOnCancellation{}监听到,能够履行一些资源的释放工作,比如这儿的撤销观察者的注册。

总结

lifecycle-runtime-ktx库中其他的扩展办法我们都比较了解,这儿就不再额定进行介绍了,感兴趣的能够参阅我下面写的几篇文章(包括源码剖析):

Jetpack实践攻略:lifecycle与协程的”猫腻”事(一)

Jetpack实践攻略:lifecycle与协程的”猫腻”事(二)