Jetpack Compose 中如何在 Composable 可组合函数中获取当前 Activity?

介绍

使用 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 的方法,推荐使用第三种官方定义使用的方法,或在调用不复杂的情况下使用第一种方法。


作者:ITmob
来源:ITmob.cn
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×