携手创作,一起生长!这是我参与「日新方案 8 月更文挑战」的第6天,点击检查活动概况
本篇文章主要是介绍
lifecycle-runtime-ktx
的两个我们用的比较少的API:findViewTreeLifecycleOwner
和withCreated/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
是在哪里赋值的:
看下AppCompatActivity
的setContentView()
办法:
@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
并决议被挂起的协程的康复机遇。
-
监听界面状况必定需求添加观察者将协程与界面生命周期绑定,所以这步便是
添加观察者
; -
观察者主要干了两件事情:
-
收到界面销毁
ON_DESTROY
,抛出反常LifecycleDestroyedException
,并康复挂起协程的履行; -
到达指定的
Started
状况,直接履行运用runCatching
(捕捉反常)包裹的协程代码块,拿到成果后调用resumeWith
康复被挂起的协程;
- 协程被撤销则撤销观察者注册
这个便是为了兜底,一般都是建议运用Activity的lifecycleScope
或viewModel
的viewModelScope
作为协程效果域,因为这两个是和对应组件的生命周期绑定的,这样当组件销毁/清楚,该效果域下的子协程就会被撤销,咱们经过invokeOnCancellation{}
监听到,能够履行一些资源的释放工作,比如这儿的撤销观察者的注册。
总结
lifecycle-runtime-ktx
库中其他的扩展办法我们都比较了解,这儿就不再额定进行介绍了,感兴趣的能够参阅我下面写的几篇文章(包括源码剖析):
Jetpack实践攻略:lifecycle与协程的”猫腻”事(一)
Jetpack实践攻略:lifecycle与协程的”猫腻”事(二)