You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

App Actions OPEN_APP_FEATURE热启动参数读取异常问题

问题背景

我正在学习Google官方Codelab教程《Extend an Android app to the Google Assistant with App Actions (Level 2)》,当前进度到「Add the Open app feature BII capability」步骤,已经按照教程要求完成shortcuts.xml的相关配置。
我分别在两类设备上完成测试:

  • 真机:运行Android 11、内存3664M的Motorola Moto G Power
  • 虚拟设备:运行Android 12(API 31)的Pixel 5,分别测试1536MB、4096MB两种内存配置

测试中观察到的现象如下:

  1. App Action测试工具内的功能验证全部正常
  2. 首次通过Google Assistant唤起应用时,可以正确根据传入的feature参数展示对应内容
  3. 返回Android主屏幕后再次通过Assistant唤起、传入不同feature参数时,应用会被拉起到前台,但经常展示首次唤起时的旧内容;只有手动在最近任务列表划掉应用关闭进程后,唤起才能100%正常;仅返回主屏幕不杀进程的场景下,功能偶发正常、偶发异常。
初步排查过程

初步判断异常原因是应用未被销毁时TasksActivity.onCreate()不会被调用:我给onCreate()、onStart()、onResume()、onPause()、onStop()几个生命周期方法加了日志之后验证了这个猜想——原有逻辑写在onCreate()里构建包含TasksFragment的布局,且TasksFragment.onCreateView()只有在TasksActivity.onCreate()触发时才会执行,代码通过activity?.intent?.extras获取feature参数。
在做Android开发的朋友建议下,我新增了TasksFragment.onResume()逻辑,在视图重新展示时再次读取activity?.intent?.extras获取feature参数。日志验证显示热启动场景下onResume()确实会触发(此时onCreate()/onCreateView()都不执行),但读取到的feature参数还是旧值,判断是Activity持有的Intent对象没有更新为新唤起的Intent。

待解决问题

不管是应用冷启动,还是应用已经在后台运行的热启动场景,怎么才能正确捕获最新的Intent和对应参数?是否需要调整Android生命周期的处理位置,把参数处理逻辑从onCreate()迁移到其他生命周期方法?


附:调试日志与代码片段

首次冷启动唤起日志

首次使用指令「show my completed tasks using AppName」唤起应用时的日志,注意日志中触发了onCreateView和onCreate,且feature=completed_tasks:

2022-07-12 20:07:55.912 18392-18392/com.addventure.appactions.common D/TasksFragment: >> onCreateView
2022-07-12 20:07:55.912 18392-18392/com.addventure.appactions.common D/TasksFragment: [feature=completed_tasks]
2022-07-12 20:07:55.912 18392-18392/com.addventure.appactions.common D/TasksFragment: [com.google.android.apps.gsa.shared.util.starter.IntentStarter.ERROR_TOAST_ID=2132082905]
2022-07-12 20:07:55.912 18392-18392/com.addventure.appactions.common D/TasksFragment: [extra_accl_intent=true]
2022-07-12 20:07:55.987 18392-18392/com.addventure.appactions.common D/TasksActivity: >> onCreate
2022-07-12 20:07:55.988 18392-18392/com.addventure.appactions.common D/TasksActivity: [feature=completed_tasks]
2022-07-12 20:07:55.988 18392-18392/com.addventure.appactions.common D/TasksActivity: [com.google.android.apps.gsa.shared.util.starter.IntentStarter.ERROR_TOAST_ID=2132082905]
2022-07-12 20:07:55.988 18392-18392/com.addventure.appactions.common D/TasksActivity: [extra_accl_intent=true]
2022-07-12 20:07:56.012 18392-18392/com.addventure.appactions.common D/TasksActivity: >> onStart
2022-07-12 20:07:56.018 18392-18392/com.addventure.appactions.common D/TasksActivity: >> onResume
2022-07-12 20:07:56.020 18392-18392/com.addventure.appactions.common D/TasksFragment: >> onResume
2022-07-12 20:07:56.020 18392-18392/com.addventure.appactions.common D/TasksFragment: [feature=completed_tasks]
2022-07-12 20:07:56.020 18392-18392/com.addventure.appactions.common D/TasksFragment: [com.google.android.apps.gsa.shared.util.starter.IntentStarter.ERROR_TOAST_ID=2132082905]
2022-07-12 20:07:56.020 18392-18392/com.addventure.appactions.common D/TasksFragment: [extra_accl_intent=true]

返回主屏幕日志

2022-07-12 20:09:20.073 18392-18392/com.addventure.appactions.common D/TasksActivity: >> onPause
2022-07-12 20:09:20.124 18392-18392/com.addventure.appactions.common D/TasksActivity: >> onStop

热启动唤起日志

返回主屏幕后使用指令「show my open tasks using AppName」重新唤起应用时的日志,注意日志中未打印onCreate和onCreateView,且feature=completed_tasks,和本次唤起应该传入的active_tasks参数不符:

2022-07-12 20:09:22.130 18392-18392/com.addventure.appactions.common D/TasksActivity: >> onStart
2022-07-12 20:09:22.132 18392-18392/com.addventure.appactions.common D/TasksActivity: >> onResume
2022-07-12 20:09:22.132 18392-18392/com.addventure.appactions.common D/TasksFragment: >> onResume
2022-07-12 20:09:22.133 18392-18392/com.addventure.appactions.common D/TasksFragment: [feature=completed_tasks]
2022-07-12 20:09:22.133 18392-18392/com.addventure.appactions.common D/TasksFragment: [com.google.android.apps.gsa.shared.util.starter.IntentStarter.ERROR_TOAST_ID=2132082905]
2022-07-12 20:09:22.133 18392-18392/com.addventure.appactions.common D/TasksFragment: [extra_accl_intent=true]

shortcuts.xml配置

<!--    OPEN APP FEATURE with inline inventory
    注意:请将com.addventure.appactions.common替换为你在app/build.gradle中配置的application ID
-->
<!-- Add Get Thing BII shortcuts   -->
<shortcut
    android:shortcutId="active_tasks"
    android:shortcutShortLabel="@string/label_active"
    android:enabled="false">
    <capability-binding
        android:key="actions.intent.OPEN_APP_FEATURE">
        <parameter-binding
            android:key="feature"
            android:value="@array/active_tasks_synonyms" />
    </capability-binding>
</shortcut>

<shortcut
    android:shortcutId="completed_tasks"
    android:shortcutShortLabel="@string/label_completed"
    android:enabled="false">
    <capability-binding
        android:key="actions.intent.OPEN_APP_FEATURE">
        <parameter-binding
            android:key="feature"
            android:value="@array/completed_tasks_synonyms" />
    </capability-binding>
</shortcut>

<!-- Add Open App Feature BII capability   -->
<capability android:name="actions.intent.OPEN_APP_FEATURE">
    <intent
        android:action="android.intent.action.VIEW"
        android:targetPackage="com.addventure.appactions.common"
        android:targetClass="com.example.android.architecture.blueprints.todoapp.tasks.TasksActivity">
        <parameter
            android:name="feature"
            android:key="feature"/>
    </intent>
</capability>

TasksActivity.kt代码

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.tasks_act)
    setupNavigationDrawer()
    setSupportActionBar(findViewById(R.id.toolbar))

    // 问题排查用日志
    log(TAG, "onCreate", intent)

    val navController: NavController = findNavController(R.id.nav_host_fragment)
    appBarConfiguration =
        AppBarConfiguration.Builder(R.id.tasks_fragment_dest, R.id.statistics_fragment_dest)
            .setOpenableLayout(drawerLayout)
            .build()
    setupActionBarWithNavController(navController, appBarConfiguration)
    findViewById<NavigationView>(R.id.nav_view)
        .setupWithNavController(navController)
}

override fun onStart() {
    super.onStart()
    log(TAG, "onStart")
}

override fun onResume() {
    super.onResume()
    log( TAG, "onResume")
}

override fun onPause() {
    super.onPause()
    log( TAG, "onPause" )
}

override fun onStop() {
    super.onStop()
    log( TAG, "onStop" )
}

override fun onDestroy(){
    super.onDestroy()
    log( TAG, "onDestroy" )
}

companion object {
    fun log( tag: String, f: String ){
        log( tag, f, null )
    }

    fun log(tag: String, f: String, intent: Intent?) {
        Log.d(tag, ">> $f")
        val bundle: Bundle = intent?.extras ?: return

        bundle.keySet().forEach { key ->
            Log.d(tag, "[$key=${bundle.get(key)}]");
        }
    }
}

TasksFragment.kt代码

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    viewDataBinding = TasksFragBinding.inflate(inflater, container, false).apply {
        viewmodel = viewModel
    }
    setHasOptionsMenu(true)

    // 设置FAB点击监听
    setupFab()

    setFiltering( "onCreateView" )

    return viewDataBinding.root
}

override fun onResume() {
    super.onResume()
    setFiltering( "onResume" )
}

private fun setFiltering(f: String){
    TasksActivity.log( TAG, f, activity?.intent )
    viewModel.setFiltering(TasksFilterType.find(activity?.intent?.extras?.getString(OPEN_APP_FEATURE)))

    viewModel.setFiltering(activity?.intent?.extras?.getString(GET_THING))
}

解决方案

热启动场景下Activity的intent字段默认不会自动更新,新的唤起Intent会通过onNewIntent()回调传递,按以下步骤修改即可覆盖冷、热启动全场景:

  • 第一步:修改AndroidManifest.xml中TasksActivity的启动模式,添加android:launchMode="singleTop"属性,避免栈顶已存在的Activity实例被重复创建,保证新唤起Intent走onNewIntent回调。
  • 第二步:在TasksActivity中重写onNewIntent()方法,手动调用setIntent()更新Activity持有的Intent对象,同时通知关联的Fragment处理新参数,参考代码:
override fun onNewIntent(intent: Intent?) {
    super.onNewIntent(intent)
    // 手动更新Intent实例,否则后续调用getIntent()只会返回旧值
    setIntent(intent)
    log(TAG, "onNewIntent", intent)
    // 通知TasksFragment处理新参数
    val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)
    navHostFragment?.childFragmentManager?.fragments?.forEach { fragment ->
        if (fragment is TasksFragment) fragment.handleIntentParams(intent)
    }
}
  • 第三步:重构TasksFragment的参数处理逻辑,把原来散落在onCreateView、onResume里的参数解析、过滤条件更新逻辑抽成统一的handleIntentParams方法,冷启动初始化、热启动收到新Intent时都调用这个方法。注意不要在onResume里无条件读取Intent刷新UI,避免普通前后台切换触发不必要的重绘,可以加个布尔标记位,只有onNewIntent收到新参数时才在onResume里触发刷新。

内容的提问来源于stack exchange,提问作者Prisoner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 10:39:18