使用navigateUpFromSameTask()从活动返回

Dav*_*row 69 android android-activity android-actionbar up-button

我有两个活动,A和B.当活性A被第一次启动,它访问Intent传递给它(因为Bundlenull,因为它应该通过在第一时间),并相应地显示信息:

CustInfo m_custInfo;
...
protected void onCreate(Bundle savedInstanceState)
{
    ...
    Bundle bundle = (savedInstanceState == null) ? getIntent().getExtras() : savedInstanceState;
    m_custInfo = (CustInfo) m_bundle.getSerializable("CustInfo");
    if (m_custInfo != null
        ...
}
Run Code Online (Sandbox Code Playgroud)

这在第一次通过时工作正常.该EditText控制和ListView正确地填写.

现在,当单击列表中的项目时,将启动活动B以显示详细信息:

m_custInfo = m_arrCustomers.get(pos);

Intent intent = new Intent(A.this, B.class);
intent.putExtra("CustInfo", m_custInfo); // CustInfo is serializable
// printing this intent, it shows to have extras and no flags

startActivityForResult(intent, 1);
Run Code Online (Sandbox Code Playgroud)

在acivity B启动之前,框架调用A被覆盖onSaveInstanceState():

protected void onSaveInstanceState(Bundle outState)
{
    super.onSaveInstanceState(outState);

    outState.putSerializable("CustInfo", m_custInfo);
}
Run Code Online (Sandbox Code Playgroud)

在活动B中,当在操作栏中按下向上按钮时,我想返回活动A并使其处于以前的状态:

public boolean onOptionsItemSelected(MenuItem item)
{
    if (item.getItemId() == android.R.id.home)
    {
        Intent intent = NavUtils.getParentActivityIntent(this);
        // printing this intent, it shows to have flags but no extras

        NavUtils.navigateUpFromSameTask(this); // tried finish() here but that created an even bigger mess
        return true;
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

这就是问题,当onCreate()第二次进入活动A时,Bundle参数是null getExtras()返回null.自从onSaveInstanceState()被调用以来,我原本期望Bundle参数是非的null.

我在其他网站上看过这个问题,尝试过这些建议,但没有任何效果.

yon*_*joy 132

如果您希望应用程序以这种方式做出反应,您应该将活动A的启动模式声明为:

android:launchMode="singleTop"
Run Code Online (Sandbox Code Playgroud)

在AndroidManifest.xml中.

否则android使用标准启动模式,这意味着

"系统始终在目标任务中创建活动的新实例"

并重新创建您的活动(请参阅Android文档).

使用singleTop,系统将返回到您现有的活动(使用原始附加功能),如果它位于任务后堆栈的顶部.在这种情况下,无需实现onSaveInstanceState.

savedInstanceState在您的情况下为null,因为您的活动以前没有被操作系统关闭.

注意(谢谢,Android开发人员指向这个):

虽然这是问题的解决方案,但如果返回的活动不在后端堆栈的顶部,它将无法工作.考虑活动A的起始B的情况,它开始是C,并且C的父活动被配置为A.如果NavigateUpFromSameTask()从C 调用,则将重新创建活动,因为A不在堆栈顶部.

在这种情况下,可以使用这段代码:

Intent intent = NavUtils.getParentActivityIntent(this); 
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP); 
NavUtils.navigateUpTo(this, intent);
Run Code Online (Sandbox Code Playgroud)

  • 这迫使你让活动始终以这种方式启动.最好只在您需要它的那些特定时间进行,如下所示:http://stackoverflow.com/a/20306670/878126 (3认同)

Mar*_*ner 19

这是因为NavUtils.navigateUpFromSameTask()基本上只是打电话startActivity(intentForActivityA).如果活动A使用默认值android:launchMode="standard",则会创建活动的新实例,并且不使用保存的状态.有三种方法可以解决这个问题,各种优点和缺点.

选项1

覆盖活动B中的getParentActivityIntent()并指定活动A所需的适当额外内容:

@Override
public Intent getParentActivityIntent() {
    Intent intent = super.getParentActivityIntent();
    intent.putSerializable("CustInfo", m_custInfo);
    return intent;
}
Run Code Online (Sandbox Code Playgroud)

注意:如果您正在使用ActionBarActivity和支持库中的工具栏,那么您将需要覆盖getSupportParentActivityIntent().

我认为这是最优雅的解决方案,因为无论用户如何导航到活动B,它都能正常工作.如果用户直接导航到活动B而不通过活动A,则下面列出的两个选项无法正常工作,例如,如果活动B从通知或URL处理程序启动.我认为这种情况是"向上"导航API复杂的原因,并不仅仅是一个愚蠢的后退按钮.

该解决方案的一个缺点是使用标准的开始 - 新活动过渡动画而不是返回到先前活动的动画.

选项2

拦截了按钮,并把它作为通过重写一个愚蠢的后退按钮onNavigateUp() :

@Override
public boolean onNavigateUp() {
    onBackPressed();
    return true;
}
Run Code Online (Sandbox Code Playgroud)

注意:如果您正在使用ActionBarActivity和支持库中的工具栏,那么您将需要覆盖onSupportNavigateUp().

这个解决方案有点hacky,只适用于"up"和"back"应该表现相同的应用程序.这可能是罕见的,因为如果"向上"和"向后"应该表现相同,那么为什么还要打扰按钮呢?该解决方案的一个优点是使用标准的返回先前活动动画.

选项3

正如yonojoy所建议的,android:launchMode="singleTop"请参加活动A.详情请见他的回答.请注意,这singleTop不适用于所有应用程序.你必须尝试和/或花一些时间阅读文档来自己决定.