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 OnNewIntentProvider 为 ComponentActivity 提供了监听 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?