31

源代码可在此处获得:https ://github.com/novemberox/NavigationTest此示例的修改版本:http: //developer.android.com/training/implementing-navigation/ancestral.html

我有三个活动:

  • Main Activity只是应用程序的主要入口
  • Category Activity它是 Detail Activity 的父级
  • Detail Activity

Main Activity有打开的按钮Category ActivityDetail ActivityCategory Activty只有一个按钮可以打开Detail Activity。最后Detail Activity显示一些文本并有向上按钮模拟点击 ActionBar 向上。

我的“点击”路径是:

  • 打开主要活动
  • 打开详细活动
  • 点击“向上按钮”
  • 应出现类别活动
  • 后退单击将我们移动到状态已恢复的主活动

这是预期的流程,它在 Jelly Bean 之前的每个 Android 上都运行良好(在 Galaxy Nexus 4.1.1 和模拟器 4.2 Google exp 包上测试)。它甚至适用于 ICS。我正在使用支持库和类,如 NavUtils 和 TaskStackBuilder,就像我在开始时指出的示例一样。

在 JB 上,当我单击“向上按钮”时,它会返回到 Main Activity,状态已正确恢复。我查看了支持库的源代码,发现该NavUtils.navigateUpTo方法调用了本地 JB 代码,例如Activity#navigateUpTo. 我都试过了NavUtils#navigateUpTo()NavUtils.navigateUpFromSameTask()结果同样不满意。

你有什么建议要怎么做才能有这个好的流程吗?

4

2 回答 2

63

首先,如果您的目标设备高达 API 17 (Android 4.2),targetSdkVersion请在清单中将 设置为 17。这不会破坏对旧设备的支持,它只会让新设备正常工作。当然,这并不能解决您的问题——这样做很好。

你应该使用什么?

我假设您的代码基于此页面上的祖先导航示例。您已经使用了第二个示例:

Intent upIntent = new Intent(this, MyParentActivity.class);
if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
    // This activity is not part of the application's task, so create a new task
    // with a synthesized back stack.
    TaskStackBuilder.from(this)
        .addNextIntent(new Intent(this, MyGreatGrandParentActivity.class))
        .addNextIntent(new Intent(this, MyGrandParentActivity.class))
        .addNextIntent(upIntent)
        .startActivities();
    finish();
 } else {
     // This activity is part of the application's task, so simply
     // navigate up to the hierarchical parent activity.
     NavUtils.navigateUpTo(this, upIntent);
 }

但是,对于您想要的行为,您需要替换NavUtils.navigateUpTo为:

upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(upIntent);
finish();

为什么它适用于 ICS 及更早版本?

通常,支持库试图近似于后来的 API 中引入的行为。在 的情况下NavUtils,支持库试图近似 API 16(又名 Android 4.1)中引入的行为。对于 API 16 之前的平台,NavUtils使用:

@Override
public void navigateUpTo(Activity activity, Intent upIntent) {
    upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    activity.startActivity(upIntent);
    activity.finish();
}

这会在后台堆栈中查找 中指定的活动实例upInent。如果它找到一个,它会清除它的所有内容并恢复它。否则它只会启动活动。

在 API 16+ 更高版本的平台上,支持库将事情交给ActivityAPI 中的本机调用。根据文档:

public boolean navigateUpTo (Intent upIntent)

从这个活动导航到 upIntent 指定的活动,在流程中完成这个活动。如果 upIntent 指示的活动已经存在于任务的历史中,则该活动以及历史堆栈中指示的活动之前的所有其他活动都将完成。

如果指示的活动没有出现在历史堆栈中,这将完成此任务中的每个活动,直到到达该任务的根活动,从而产生“应用内主页”行为。这在具有复杂导航层次结构的应用程序中很有用,当一个 Activity 可以通过不通过规范父 Activity 的路径到达时。

upIntent由此,如果它不在后台堆栈中,它是否会启动指定的活动尚不清楚。阅读源代码也无助于澄清事情。但是,从您的应用程序显示的行为来看,它似乎没有尝试启动该upIntent活动。

无论哪种实现是对还是错,最终结果都是您想要的FLAG_ACTIVITY_CLEAR_TOP行为,而不是原生 API 16 行为。不幸的是,这意味着您将不得不复制支持库的近似值。

哪个是对的?

免责声明:我不为 Google 工作,所以这是我的最佳猜测。

我的猜测是 API 16+ 的行为是预期的行为;它是一个内置的实现,可以访问 Android 内部,并且可以做一些通过 API 不可能完成的事情。在 API 16 之前,我认为除了使用意图标志之外,不可能以这种方式展开回栈。因此,该FLAG_ACTIVITY_CLEAR_TOP标志最接近API 16 之前的平台可用的 API 16 行为。

不幸的是,结果是在本机实现和支持库实现之间违反了您的场景中的最小惊讶原则。这让我怀疑这是否是对 API 的意外使用。即,我想知道Android是否希望您遍历活动的完整导航路径,而不是直接跳转到它。

以防万一

只是为了避免一种可能的误解,有人可能会期望shouldUpRecreateTask神奇地确定父级不在后台堆栈中,并使用TaskStackBuilder.

但是,shouldUpRecreateTask基本上确定您的活动是由您的应用程序直接启动(在这种情况下它返回false),还是它是从另一个应用程序启动的(在这种情况下它返回true)。从这本书中,支持库检查意图的“动作”是否不是ACTION_MAIN(我不完全理解),而在 API 16 平台上,它会根据任务关联性执行此检查。尽管如此,在这个应用程序的情况下,事情的结果是shouldUpRecreateTask返回 false。

于 2013-02-09T23:19:49.377 回答
0

在阅读了@cyfur01 的回答后,我想出了一个非常简单的解决方案来解决这个问题。

在您的 Activity 中覆盖此方法(或者理想情况下在某些 BaseActivity 中为所有子类修复它),它应该按预期工作:

@Override
public boolean navigateUpTo(Intent upIntent) {
    boolean result = super.navigateUpTo(upIntent);
    if (!result) {
        TaskStackBuilder.create(this)
            .addNextIntentWithParentStack(upIntent)
            .startActivities();
    }
    return result;
}

它将所有工作留给 Android 实现,然后只检查返回值navigateUpTo(upIntent)指找不到指定活动的实例并且该活动正常完成false时的返回值。当我们想要手动创建和启动父活动时,情况正是如此。另请注意,我们不必在这里调用,因为它是由 super 方法调用的。finish()

于 2017-10-13T13:58:23.333 回答