介绍
使用 Jetpack Compose 进行开发时,有时我们需要用到 Activity
实例,比如需要调用 Activity
方法的 finish()
函数来退出当前 Activity
,或获取当前 Activity
实例的 Window
属性等。
但是 Jetpack Compose 没有给出官方 API 来获取当前的 Activity
实例,本文我们将介绍几种在 Jetpack Compose 中怎样获取当前的 Activity 实例的方法。
希望快速找到答案的读者可以直接查看第三节,Google 官方的 accompanist
项目中 获取 Activity
的方法。
1. 使用方法替代直接获取 Activity
在 Composable 可组合函数中直接使用 Activity
对象并不像是最好的用法,我们可以将需要调用的 Activity
方法或属性作为参数传递给可组合函数供其调用。
package cn.itmob.composedemo
class ItmobSampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppTheme {
GetActivityInComposable(
finish = { finish() },
)
}
}
}
}
@Composable
fun GetActivityInComposable(
finish: () -> Unit,
) {
Button(onClick = finish) {
Text(text = "Finish current activity")
}
}
这种方式在可组合函数用法不复杂嵌套并不多的时候没有什么问题。但是可组合函数多次嵌套,只在个别 Composable 函数中需要使用 Activity,这样将 finish
函数作为参数层层传递也不是很好的方案。
2. Context 直接转换为 Activity
如下是源码中 LocalContext
的定义:
@Composable
@OptIn(ExperimentalComposeUiApi::class)
internal fun ProvideAndroidCompositionLocals(
owner: AndroidComposeView,
content: @Composable () -> Unit
) {
val view = owner
val context = view.context
var configuration by remember {
mutableStateOf(
context.resources.configuration,
neverEqualPolicy()
)
}
owner.configurationChangeObserver = { configuration = it }
val uriHandler = remember { AndroidUriHandler(context) }
val viewTreeOwners = owner.viewTreeOwners ?: throw IllegalStateException(
"Called when the ViewTreeOwnersAvailability is not yet in Available state"
)
val saveableStateRegistry = remember {
DisposableSaveableStateRegistry(view, viewTreeOwners.savedStateRegistryOwner)
}
DisposableEffect(Unit) {
onDispose {
saveableStateRegistry.dispose()
}
}
val imageVectorCache = obtainImageVectorCache(context, configuration)
CompositionLocalProvider(
LocalConfiguration provides configuration,
// LocalContext 的是 AndroidComposeView 的上下文
LocalContext provides context,
LocalLifecycleOwner provides viewTreeOwners.lifecycleOwner,
LocalSavedStateRegistryOwner provides viewTreeOwners.savedStateRegistryOwner,
LocalSaveableStateRegistry provides saveableStateRegistry,
LocalView provides owner.view,
LocalImageVectorCache provides imageVectorCache
) {
ProvideCommonCompositionLocals(
owner = owner,
uriHandler = uriHandler,
content = content
)
}
}
而这个上下文(Context
)跟传统使用 View
进行编程类似 View
的上下文就是当前的 Activity,我们可以直接将其转换为 Activity
类型。
@Composable
fun GetActivityInComposable() {
val activity = LocalContext.current as Activity
Button(
onClick = {
activity.finish()
},
) {
Text(text = "Finish current activity")
}
}
从源码看这种方式获取 Activity 是安全的,但 Google 官方的 accompanist 项目中不是通过这种方式来获取 Activity 的。而是通过下文的方式获取的,从代码看直接对 Context 进行转换是存在问题的,看来之后需要再仔细看看源码的实现。
3. 通过 Context 获取 Activity
Google 官方的 accompanist 项目中获取 Activity
是通过定义了一个 Context
的扩展函数实现的:
/**
* Find the closest Activity in a given Context.
*/
internal fun Context.findActivity(): Activity {
var context = this
while (context is ContextWrapper) {
if (context is Activity) return context
context = context.baseContext
}
throw IllegalStateException("Permissions should be called in the context of an Activity")
}
以上源码的链接地址:PermissionUtil.kt
总结
上文介绍了三种获取或调用 Activity
的方法,推荐使用第三种官方定义使用的方法,或在调用不复杂的情况下使用第一种方法。