ITmob-Ly
发布于 2023-09-26 / 151 阅读
0

在 Android 中使用 Jetpack Compose 时如何处理 onNewIntent?

How to handle new intent when using Jetpack Compose in Android?


Android 中当应中要多次调用某个页面而不希望每次调用都重新创建 Activity 的新实例,可以通过修改 Activity 的 android:launchMode 来实现。

Activity 有五种启动模式,除默认的启动模式(standard)每次调用都会重新创建 Activity 的新实例,其他四种模式下都有各自相应的特性,在某些情况下可实现 Activity 的复用,比如:launchMode 是 singleTop 时如果要调用的 Activity 在当前任务栈中顶部则重用该实例。

调用可复用的 Activity 时,它不会每次调用都重新创建并执行 onCreate 方法,因此调用它时通过 Intent 传的参数需要通过 onNewContent 方法来处理,这样即使调用 Activity 时,即使它不重新创建我们也能通过 onNewContent 来获取此次调用传递的新参数。

AndroidManifest.xml

<activity
    android:name=".MainActivity"
    android:exported="true"
    android:launchMode="singleTask"
    android:theme="@style/Theme.MyAppTheme">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

MainActivity.kt

package cn.itmob.demo

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // TODO 处理 Activity 的创建
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        // TODO 处理 Activity 的复用
    }
}

上面介绍了使用 Activity 时为什么要使用 onNewIntent 和怎样使用它。在开发多 Activity 应用时每个页面都是一个单独的 Activity,在每个 Activity 的 onNewIntent 中处理复用逻辑即可,但我们使用 Jetpack Compose 开发时,更推荐的是使用单 Activity 的方式实现,整个应用只存在一个 Activity,每个页面都是由可组合项 Composable 方法渲染的。

在 Composable 方法中怎样像 Activity 中一样处理 onNewIntent 呢?

Jetpack Compose 1.8.0-alpha02 开始增加了新的 API OnNewIntentProviderComponentActivity 提供了监听 new intent 的方法:addOnNewIntentListener

我们先来看看 addOnNewIntentListener 的用法:

package cn.itmob.demo

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {

                DisposableEffect(Unit) {
                    val listener = Consumer<Intent> {
                        // 处理 new intent
                    }
                    // 创建 Activity 时增加 new intent 监听
                    addOnNewIntentListener(listener)
                    // Activity 销毁时移除 new intent 监听
                    onDispose { removeOnNewIntentListener(listener) }
                }

                MainApp() {
                    // TODO 页面内容
                }

            }
    }
}

上例是在 MainActivity 中如果可组合项不在 Activity 中定义怎样调用 addOnNewIntentListener 呢?

例如在名为 MainScreen.kt 的文件中创建用于渲染首页的 Composable 可组合项:

先获取当前的 Activity 再增加 new intent 监听

MainScreen.kt

package cn.itmob.demo

@Composable
fun MainContent() {
    val context = LocalContext.current
    DisposableEffect(Unit) {
        val listener = Consumer<Intent> { intent ->
            // TODO 处理 new intent
            val action = intent.action
        }
        // 渲染时增加 new intent 监听
        context.findComponentActivity().addOnNewIntentListener(listener)
        // 销毁时移除 new intent 监听
        onDispose { context.findComponentActivity().removeOnNewIntentListener(listener) }
    }

    Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
        Greeting("Hello https://itmob.cn")
    }
}

/**
 * Find the closest ComponentActivity in a given Context.
 */
internal fun Context.findComponentActivity(): ComponentActivity {
    var context = this
    while (context is ContextWrapper) {
        if (context is ComponentActivity) return context
        context = context.baseContext
    }
    throw IllegalStateException("Permissions should be called in the context of an Activity")
}

上面代码中获取 Activity 的部分,可以参加另一篇文章:Jetpack Compose 中如何在 Composable 可组合函数中获取当前 Activity?