我有两个活动,A和B。 首次启动活动 A 时,它会访问传递给它的Intent
(因为Bundle
是null
的,因为它应该是第一次通过),并相应地显示信息:
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
...
}
这在第一次通过时工作正常。 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);
在 Acivity B 启动之前,框架调用 A 的重写onSaveInstanceState()
:
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putSerializable("CustInfo", m_custInfo);
}
在活动 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;
}
...
}
这就是问题所在,当第二次onCreate()
活动 A 时,Bundle
参数null
并且getExtras()
返回 null
。 由于调用了onSaveInstanceState()
,因此我本以为Bundle
参数是非null
的。
我已经在其他网站上阅读了这个问题,尝试了这些建议,但没有任何效果。
如果希望应用程序以这种方式做出反应,则应将活动 A 的启动模式声明为:
android:launchMode="singleTop"
在你的 AndroidManifest.xml 中。
否则安卓使用标准启动模式,这意味着
"系统始终在 目标任务"
系统会重新创建您的活动记录(请参阅 Android 文档)。
使用 singleTop,如果系统位于任务后堆栈的顶部,则系统会返回到您的现有活动(带有原始的额外活动)。在这种情况下,不需要实现 onSaveInstanceState。
在您的情况下,savedInstanceState 为空,因为您的活动以前未作系统关闭。
注意(感谢安卓开发者指出这一点):
虽然这是问题的解决方案,但如果返回的活动不在后退堆栈的顶部,它将不起作用。考虑活动 A 开始 B 的情况,它开始 C,而 C 的父活动配置为 A。如果从 C 调用 NavigateUpFromSameTask()
,则将重新创建活动,因为 A 不在堆栈顶部。
在这种情况下,可以改用这段代码:
Intent intent = NavUtils.getParentActivityIntent(this);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
NavUtils.navigateUpTo(this, intent);
是因为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;
}
注意:如果您使用的是 AppCompatActivity 和 androidx 或支持库中的工具栏,则需要覆盖 getSupportParentActivityIntent()。
我觉得这是最优雅的解决方案,因为无论用户如何导航到活动 B,它都可以工作。如果用户直接导航到活动 B 而不通过活动 A,例如,如果活动 B 从通知或 URL 处理程序启动,则下面列出的两个选项将无法正常工作。我认为这种情况是"向上"导航 API 很复杂并且不仅仅充当哑后退按钮的原因。
此解决方案的一个缺点是使用标准的开始新活动过渡动画,而不是返回到上一个活动动画。
选项 2
拦截向上按钮,并通过覆盖 onNavigateUp() 将其视为哑后退按钮:
@Override
public boolean onNavigateUp() {
onBackPressed();
return true;
}
注意:如果您使用的是 AppCompatActivity 和 androidx 或支持库中的工具栏,则需要覆盖 onSupportNavigateUp()。
这个解决方案有点笨拙,仅适用于"向上"和"后退"行为相同的应用程序。这可能很少见,因为如果"向上"和"后退"的行为应该相同,那么为什么要费心使用向上按钮呢?此解决方案的一个优点是使用标准的返回到上一个活动动画。
选项 3
按照约诺乔伊的建议,android:launchMode="singleTop"
活动A。详见他的回答。请注意,singleTop
并不适合所有应用程序。您必须尝试一下和/或花一些时间阅读文档以自己决定。
而不是调用NavUtils.navigateUpFromSameTask
您可以获取父意图并在导航之前对其进行修改。例如:
Intent intent = NavUtils.getParentActivityIntent(this);
intent.putExtra("CustInfo", m_custInfo); // CustInfo is serializable
NavUtils.navigateUpTo(this, intent);
这将与NavUtils.navigateUpFromSameTask
完全相同,但允许您向意向添加一些额外的属性。您可以查看代码NavUtils.navigateUpFromSameTask
它非常简单。
public static void navigateUpFromSameTask(Activity sourceActivity) {
Intent upIntent = getParentActivityIntent(sourceActivity);
if (upIntent == null) {
throw new IllegalArgumentException("Activity " +
sourceActivity.getClass().getSimpleName() +
" does not have a parent activity name specified." +
" (Did you forget to add the android.support.PARENT_ACTIVITY <meta-data> " +
" element in your manifest?)");
}
navigateUpTo(sourceActivity, upIntent);
}
将父活动设为SINGLE_TOP可能适用于某些简单的应用程序,但如果此活动不是直接从其父级启动的呢?或者,您可能合法地希望父级存在于后退堆栈中的多个位置。
我认为这是一个更通用的解决方案,可以添加到您的基本活动类中:
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
if (item.getItemId() == android.R.id.home) {
final Intent upIntent = NavUtils.getParentActivityIntent(this);
if (upIntent == null)
onBackPressed();
else
//optionally add this flag to the intent: Intent.FLAG_ACTIVITY_SINGLE_TOP
NavUtils.navigateUpTo(this, upIntent);
return true;
}
return super.onOptionsItemSelected(item);
}
对于您希望导航到某个特定父活动的每个活动,请在清单上使用以下命令:
<activity android:parentActivityName="pathToParentActivity" ...>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="pathToParentActivity"/>
</activity>
而且,如果您的 minSdk 来自 API 16,请删除"元数据"标记,以便在清单中保留以下内容:
<activity android:parentActivityName="pathToParentActivity" ...>
</activity>
更多信息在这里
你提到:
在活动 B 中,当在操作栏中按下"向上"按钮时,我想返回到活动 A 并使其处于与之前相同的状态。
如果您在活动 A 上的内容驻留在片段中,请在片段的 onCreate() 中调用 setRetainInstance(true)。
然后,片段实例将在活动重新创建中保留。