如何在Android Studio中预览Android Glance App Widget?
Android Glance Widget预览报错解决方法
问题描述
我用Glance创建Widget,UI写在MyContent可组合函数里,加了这段预览代码:
@Preview(showBackground = true) @Composable fun ContentPreview() { MyContent() }
结果报错Failed to instantiate Composition Local,提示This preview was unable to find a CompositionLocal. You might need to define it so it can render correctly.。Widget能在物理设备正常运行,但Android Studio里就是没法预览。
完整栈追踪:
java.lang.IllegalStateException: Invalid applier at androidx.compose.runtime.ComposablesKt.invalidApplier(Composables.kt:472) at androidx.glance.layout.ColumnKt.Column-K4GKKTE(Column.kt:102) at com.example.myproject.MyAppWidgetKt.MyContent(MyAppWidget.kt:46) at com.example.myproject.MyAppWidgetKt.ContentPreview(MyAppWidget.kt:69) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at androidx.compose.ui.tooling.ComposableInvoker.invokeComposableMethod(ComposableInvoker.kt:163) at androidx.compose.ui.tooling.ComposableInvoker.invokeComposable(ComposableInvoker.kt:203) at androidx.compose.ui.tooling.ComposeViewAdapter$init$3$1$composable$1.invoke(ComposeViewAdapter.kt:509) at androidx.compose.ui.tooling.ComposeViewAdapter$init$3$1$composable$1.invoke(ComposeViewAdapter.kt:507) at androidx.compose.ui.tooling.ComposeViewAdapter$init$3$1.invoke(ComposeViewAdapter.kt:544) at androidx.compose.ui.tooling.ComposeViewAdapter$init$3$1.invoke(ComposeViewAdapter.kt:502) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35) at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228) at androidx.compose.ui.tooling.InspectableKt.Inspectable(Inspectable.kt:61) at androidx.compose.ui.tooling.ComposeViewAdapter$WrapPreview$1.invoke(ComposeViewAdapter.kt:449) at androidx.compose.ui.tooling.ComposeViewAdapter$WrapPreview$1.invoke(ComposeViewAdapter.kt:448) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35) at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228) at androidx.compose.ui.tooling.ComposeViewAdapter.WrapPreview(ComposeViewAdapter.kt:443) at androidx.compose.ui.tooling.ComposeViewAdapter.access$WrapPreview(ComposeViewAdapter.kt:127) at androidx.compose.ui.tooling.ComposeViewAdapter$init$3.invoke(ComposeViewAdapter.kt:502) at androidx.compose.ui.tooling.ComposeViewAdapter$init$3.invoke(ComposeViewAdapter.kt:499) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35) at androidx.compose.ui.platform.ComposeView.Content(ComposeView.android.kt:428) at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:252) at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:251) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35) at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228) at androidx.compose.ui.platform.CompositionLocalsKt.ProvideCommonCompositionLocals(CompositionLocals.kt:195) at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:119) at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:118) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35) at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228) at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.ProvideAndroidCompositionLocals(AndroidCompositionLocals.android.kt:110) at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$2.invoke(Wrapper.android.kt:158) at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$2.invoke(Wrapper.android.kt:157) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35) at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228) at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:157) at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:142) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35) at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm.jvm.kt:78) at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3340) at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:3273) at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:588) at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:1013) at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:520) at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:142) at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:133) at androidx.compose.ui.platform.AndroidComposeView.setOnViewTreeOwnersAvailable(AndroidComposeView.android.kt:1191) at androidx.compose.ui.platform.WrappedComposition.setContent(Wrapper.android.kt:133) at androidx.compose.ui.platform.WrappedComposition.onStateChanged(Wrapper.android.kt:183) at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.kt:314) at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.kt:192) at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:140) at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:133) at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(AndroidComposeView.android.kt:1266) at android.view.View.dispatchAttachedToWindow(View.java:21291) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3491) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498) at android.view.AttachInfo_Accessor.setAttachInfo(AttachInfo_Accessor.java:54) at com.android.layoutlib.bridge.impl.RenderSessionImpl.inflate(RenderSessionImpl.java:372) at com.android.layoutlib.bridge.Bridge.createSession(Bridge.java:450) at com.android.tools.idea.layoutlib.LayoutLibrary.createSession(LayoutLibrary.java:122) at com.android.tools.rendering.RenderTask.createRenderSession(RenderTask.java:742) at com.android.tools.rendering.RenderTask.lambda$inflate$7(RenderTask.java:889) at com.android.tools.rendering.RenderExecutor$runAsyncActionWithTimeout$3.run(RenderExecutor.kt:202) at com.android.tools.rendering.RenderExecutor$PriorityRunnable.run(RenderExecutor.kt:316) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:833)
MyAppWidget.kt完整代码:
import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.glance.GlanceId import androidx.glance.GlanceModifier import androidx.glance.appwidget.GlanceAppWidget import androidx.glance.appwidget.provideContent import androidx.glance.layout.Column import androidx.glance.text.Text import androidx.glance.Button import androidx.glance.action.actionStartActivity import androidx.glance.appwidget.action.actionStartActivity import androidx.glance.layout.Alignment import androidx.glance.layout.Column import androidx.glance.layout.Row import androidx.glance.layout.fillMaxSize import androidx.glance.layout.padding import androidx.glance.text.Text /* Import Glance Composables In the event there is a name clash with the Compose classes of the same name, you may rename the imports per https://kotlinlang.org/docs/packages.html#imports using the `as` keyword. */ class MyAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { // Load data needed to render the AppWidget. // Use `withContext` to switch to another thread for long running // operations. provideContent { // create your AppWidget here MyContent() } } } @Composable private fun MyContent() { Column( modifier = GlanceModifier.fillMaxSize(), verticalAlignment = Alignment.Top, horizontalAlignment = Alignment.CenterHorizontally ) { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) Row(horizontalAlignment = Alignment.CenterHorizontally) { Button( text = "Home", onClick = actionStartActivity<MainActivity>() ) Button( text = "Work", onClick = actionStartActivity<MainActivity>() ) } } } @Preview(showBackground = true) @Composable fun ContentPreview() { MyContent() }
原因
Glance的可组合函数依赖自己专属的Composition Local和Applier,普通Compose预览环境没提供这些,直接预览就会找不到必要的上下文,报"Invalid applier"错误。
解决步骤
- 导入Glance专属的预览包:
import androidx.glance.appwidget.preview.GlancePreview
- 把原来的
@Preview换成@GlancePreview,预览函数不用改内容:
@GlancePreview @Composable fun ContentPreview() { MyContent() }
- (可选)如果Glance组件和普通Compose组件重名(比如
Column、Text),可以用as重命名导入避免冲突:
import androidx.glance.layout.Column as GlanceColumn import androidx.glance.text.Text as GlanceText
修改后的完整代码
import android.content.Context import androidx.compose.runtime.Composable import androidx.glance.GlanceId import androidx.glance.GlanceModifier import androidx.glance.appwidget.GlanceAppWidget import androidx.glance.appwidget.provideContent import androidx.glance.appwidget.preview.GlancePreview import androidx.glance.layout.Alignment import androidx.glance.layout.Column import androidx.glance.layout.Row import androidx.glance.layout.fillMaxSize import androidx.glance.layout.padding import androidx.glance.text.Text import androidx.glance.Button import androidx.glance.action.actionStartActivity class MyAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { provideContent { MyContent() } } } @Composable private fun MyContent() { Column( modifier = GlanceModifier.fillMaxSize(), verticalAlignment = Alignment.Top, horizontalAlignment = Alignment.CenterHorizontally ) { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) Row(horizontalAlignment = Alignment.CenterHorizontally) { Button( text = "Home", onClick = actionStartActivity<MainActivity>() ) Button( text = "Work", onClick = actionStartActivity<MainActivity>() ) } } } @GlancePreview @Composable fun ContentPreview() { MyContent() }
改完之后Android Studio就能正常渲染Glance Widget的预览了。
内容的提问来源于stack exchange,提问作者karatekraft
相关产品推荐
相关产品推荐

