引言:为什么 Jetpack Compose 中的 Context 是初学者的痛点?
\n如果您曾经使用 XML 在 Android 上编写过代码,您就会知道 Context 实际上是通往一切的“钥匙”:从访问资源到启动新的 Activity。但是当您转向 Jetpack Compose 时,一切都变了。可组合函数无法直接访问 Context,这常常成为开发者的绊脚石,尤其是那些刚开始学习声明式方法的开发者。
在 Stack Overflow 上,“How to get Context in Jetpack Compose” 这个问题获得了数千次浏览和数十个答案。这并非偶然。在本文中,我们将介绍在 Compose 中获取 Context 的所有可能方法,从简单到高级,并附有 Kotlin 的实际代码示例。您将了解何时使用 LocalContext.current,如何避免内存泄漏,以及哪些实践被认为是 2025 年的“最佳实践”。
1. 最简单的方法:LocalContext.current
\n如果您需要在可组合函数内部获取 Context,最直接的方法是使用 LocalContext.current。这是一个可组合的局部变量,提供当前的 Context(通常是 Activity 或 ContextWrapper)。
@Composable\nfun MyScreen() {\n val context = LocalContext.current\n \n Button(onClick = {\n Toast.makeText(context, "来自 Compose 的问候!", Toast.LENGTH_SHORT).show()\n }) {\n Text("显示 Toast")\n }\n}\n\n这段代码可以工作,但有一个细微之处:LocalContext.current 提供的是绑定到当前 CompositionLocal 的 Context。在大多数情况下,这正是您所需要的。但请记住,不应将此 Context 传递给 ViewModel 或保存在长期存在的对象中——这可能导致内存泄漏。
何时使用?
\n- \n
- 用于 UI 中的一次性操作:Toast、Snackbar、启动 Activity。 \n
- 用于访问资源:
context.getString(R.string.app_name)。 \n - 用于与系统服务交互:
context.getSystemService()。 \n
2. ViewModel 中的 Context:正确的方法
\n最常见的错误之一是将 Context 直接传递给 ViewModel。在 Android Architecture Components 中,这被视为反模式,因为 ViewModel 可能比 Activity 存活得更久,导致 Context 变得过时。相反,请使用 AndroidViewModel。
class MyViewModel(application: Application) : AndroidViewModel(application) {\n \n private val context = getApplication<Application>().applicationContext\n \n fun loadData() {\n // 使用 applicationContext,这是安全的\n val prefs = context.getSharedPreferences("my_prefs", Context.MODE_PRIVATE)\n }\n}\n\n在 Compose 中,您可以通过标准方式获取此类 ViewModel 的实例:
\n\n@Composable\nfun MyScreen() {\n val viewModel: MyViewModel = viewModel()\n // Context 已在 ViewModel 内部,无需从 UI 传递\n}\n\n优点: applicationContext 在进程存活期间一直存在,并且不依赖于 Activity 的生命周期。这是安全的,并且是 Google 推荐的。
3. CompositionLocal 中的 Context:自定义提供者
\n有时您需要传递特定的 Context(例如,用于测试或用于包含库的模块)。在这种情况下,您可以创建自己的 CompositionLocal。
val LocalCustomContext = staticCompositionLocalOf<Context> {\n error("未提供自定义 context")\n}\n\n@Composable\nfun CustomContextProvider(context: Context, content: @Composable () -> Unit) {\n CompositionLocalProvider(LocalCustomContext provides context) {\n content()\n }\n}\n\n// 使用方式:\n@Composable\nfun MyScreen() {\n val customContext = LocalCus