在OverlayPlugin中使用Jetpack Compose定制导航栏遇上下文错误
问题背景
我正在尝试使用Jetpack Compose定制SystemUI导航栏,这类场景无法像普通应用那样直接使用Activity或Fragment,必须通过实现OverlayPlugin接口来完成,同时在AndroidManifest.xml中声明对应的Service,代码如下:
<service android:name=".SampleOverlayPlugin" android:label="@string/plugin_label" android:exported="false"> <intent-filter> <action android:name="com.android.systemui.action.PLUGIN_OVERLAY" /> </intent-filter> </service>
遇到的错误
在使用提供的上下文初始化Compose相关组件时,触发了以下异常:
Tried to access the API:ViewConfiguration which needs to have proper configuration from a non-UI
Context:com.android.documentsui.DocumentsApplication@608ad35 The API:ViewConfiguration needs a proper configuration. Use UI contexts such as an activity or a context created via createWindowContext(Display, int, Bundle) or createConfigurationContext(Configuration) with a proper configuration.
java.lang.IllegalAccessException: Tried to access the API:ViewConfiguration which needs to have proper configuration from a non-UI Context:com.android.documentsui.DocumentsApplication@608ad35
已尝试但无效的方案
- 针对InputMethodService中ComposeView无法传递ViewTreeLifecycleOwner的修复方案
- Android Service中集成Jetpack Compose的通用适配方案
解决方案
这个错误的核心是使用了非UI上下文访问需要配置信息的View相关API,需要为Compose创建符合要求的UI上下文:
- 创建带正确配置的UI上下文
在OverlayPlugin的实现类中,不要直接使用传入的Application上下文,而是通过createWindowContext创建关联当前显示设备的UI上下文,并指定符合SystemUI overlay要求的窗口类型:
// 获取当前Display,也可以根据SystemUI提供的Display信息调整 val display = context.display ?: Display.DEFAULT_DISPLAY // 创建UI上下文,窗口类型根据导航栏场景选择,比如TYPE_SYSTEM_UI_OVERLAY或TYPE_NAVIGATION_BAR_PANEL val windowContext = context.createWindowContext( display, WindowManager.LayoutParams.TYPE_SYSTEM_UI_OVERLAY, null )
- 使用UI上下文初始化ComposeView
创建ComposeView时传入上述创建的windowContext,而非原始上下文:
val composeView = ComposeView(windowContext)
- 手动关联LifecycleOwner
由于插件运行在非Activity环境,需要手动为ComposeView设置ViewTreeLifecycleOwner,推荐使用ProcessLifecycleOwner来关联应用进程的生命周期:
import androidx.lifecycle.ProcessLifecycleOwner import androidx.compose.ui.platform.ViewTreeLifecycleOwner composeView.apply { ViewTreeLifecycleOwner.set(this, ProcessLifecycleOwner.get()) setContent { // 这里编写你的导航栏Compose UI代码 // 例如:NavigationBarUI() } }
- 正确挂载到SystemUI Overlay
将配置好的ComposeView添加到SystemUI提供的Overlay容器时,确保使用符合要求的LayoutParams,比如设置合适的宽度、高度、对齐方式,以及对应的窗口flags。
同时要注意在OverlayPlugin的生命周期回调(如onAttach、onDetach)中,正确管理ComposeView的挂载与销毁,避免内存泄漏。
内容的提问来源于stack exchange,提问作者220284

