抬头仰望星空,是否能发现自己的渺小。

伪斜杠青年

人们总是混淆了欲望和理想

Jetpack Compose Crash when adding view to window manager

背景:

错误是类似这样:

 java.lang.IllegalStateException: ViewTreeLifecycleOwner is not present in this window. Use ComponentActivity, FragmentActivity or AppCompatActivity to configure ViewTreeLifecycleOwner automatically, or call ViewTreeLifecycleOwner.set() for this View or an ancestor in the same window.

其实几个月前我刚接触时就发现了这个问题,不过那时候目的是为了 popup window,但是那时候检索到的内容不多,官方也给了 compose 版本的 popup,尝试过后就放弃折腾这个问题了。直到最近想做个悬浮窗,因为 Compose 在 UI 的圆角、背景、渐变色等的方便性,就又想试试,于是又遇到了这个错误。这次检索的关键词直接是 WindowManager add ComposeView,所以很快就解决了。

解决办法

class ComposeViewLifecycleOwner : SavedStateRegistryOwner {
private var mLifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
private var mSavedStateRegistryController: SavedStateRegistryController = SavedStateRegistryController.create(this)

/**
* @return True if the Lifecycle has been initialized.
*/
val isInitialized: Boolean
get() = true

override fun getLifecycle(): Lifecycle {
return mLifecycleRegistry
}

fun setCurrentState(state: Lifecycle.State) {
mLifecycleRegistry.currentState = state
}

fun handleLifecycleEvent(event: Lifecycle.Event) {
mLifecycleRegistry.handleLifecycleEvent(event)
}

override fun getSavedStateRegistry(): SavedStateRegistry {
return mSavedStateRegistryController.savedStateRegistry
}

fun performRestore(savedState: Bundle?) {
mSavedStateRegistryController.performRestore(savedState)
}

fun performSave(outBundle: Bundle) {
mSavedStateRegistryController.performSave(outBundle)
}
}

扩展方法

fun AbstractComposeView.addToLifecycle() {
val viewModelStore = ViewModelStore()
val lifecycleOwner = ComposeViewLifecycleOwner()
lifecycleOwner.performRestore(null)
lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
ViewTreeLifecycleOwner.set(this, lifecycleOwner)
ViewTreeViewModelStoreOwner.set(this) { viewModelStore }
ViewTreeSavedStateRegistryOwner.set(this, lifecycleOwner)
val coroutineContext = AndroidUiDispatcher.CurrentThread
val runRecomposeScope = CoroutineScope(coroutineContext)
val reComposer = Recomposer(coroutineContext)
this.compositionContext = reComposer
runRecomposeScope.launch {
reComposer.runRecomposeAndApplyChanges()
}
}

用法

val composeView = SomeComposeView(context)
composeView.addToLifecycle()
windowManager.addView(composeView, layoutParam)

遇到的坑

一开始只是单纯的把 Compose 给加上去了,但是没法重组,看评论才知道少了一部分代码(上面的代码已完整),特别贴一下:

val coroutineContext = AndroidUiDispatcher.CurrentThread
val runRecomposeScope = CoroutineScope(coroutineContext)
val recomposer = Recomposer(coroutineContext)
composeView.compositionContext = recomposer
runRecomposeScope.launch {
    recomposer.runRecomposeAndApplyChanges()
}

其他:由于没有了上层的生命周期来源,这套 lifecycle 是不完整的,如拓展函数里只有一句:

lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)

如果需要其他事件,就在合适的位置自行发送相关事件。比如我会在 detach window 的时候发 ON_DESTROY,至于其中创建的 viewmodel 自然也是需要手动管理的了。

2022.7.30 更新:

在将库更新到 compose 1.2.0 后,该代码失效:

ViewTreeSavedStateRegistryOwner.set(this, lifecycleOwner)

转为增加依赖:

implementation "androidx.savedstate:savedstate:1.2.0"

然后使用 View 的扩展函数进行替代:

setViewTreeSavedStateRegistryOwner(lifecycleOwner)

2023.3.28 更新:

将 lifecycle 相关库版本更新至 2.6.1 ,savedstate 更新至 1.2.1 后,ViewTreeLifecycleOwner 和 ViewTreeViewModelStoreOwner 便不再提供,改为扩展函数方式:

class ComposeViewModelStoreOwner: ViewModelStoreOwner{
    override val viewModelStore: ViewModelStore = ViewModelStore()
}

val viewModelStoreOwner = ComposeViewModelStoreOwner()

setViewTreeLifecycleOwner(lifecycleOwner)
setViewTreeViewModelStoreOwner(viewModelStoreOwner)

此外,顶层 lifecycle 抽象有所改变,改为了 kotlin 写法,所以 ComposeViewLifecycleOwner 中覆写的方式有所改变:

override val lifecycle: Lifecycle
get() = mLifecycleRegistry

最后整个扩展函数改为:

fun AbstractComposeView.addToLifecycle() {
val viewModelStoreOwner = ComposeViewModelStoreOwner()
val lifecycleOwner = ComposeViewLifecycleOwner()
lifecycleOwner.performRestore(null)
lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
setViewTreeLifecycleOwner(lifecycleOwner)
setViewTreeViewModelStoreOwner(viewModelStoreOwner)
setViewTreeSavedStateRegistryOwner(lifecycleOwner)
val coroutineContext = AndroidUiDispatcher.CurrentThread
val runRecomposeScope = CoroutineScope(coroutineContext)
val reComposer = Recomposer(coroutineContext)
this.compositionContext = reComposer
runRecomposeScope.launch {
reComposer.runRecomposeAndApplyChanges()
}
}

以上。

参考

https://developer.android.com/jetpack/androidx/releases/savedstate

Jetpack Compose OverlayService.


本站由以下主机服务商提供服务支持:

0条评论

发表评论