Возврат из действия с использованием navigateUpFromSameTask()
У меня есть два действия: 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);
Сразу же перед началом работы B каркас вызывает A overridden 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
.
Я читал об этой проблеме на других веб-сайтах, пробовал предложения, но ничего не работает.
Ответы
Ответ 1
Если вы хотите, чтобы ваше приложение реагировало таким образом, вы должны объявить режим запуска своей деятельности A как:
android:launchMode="singleTop"
в вашем AndroidManifest.xml.
В противном случае андроид использует стандартный режим запуска, что означает
"Система всегда создает новый экземпляр действия в целевая задача"
и ваша деятельность воссоздана (см. Документация по 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);
Ответ 2
Это происходит потому, что NavUtils.navigateUpFromSameTask()
в основном просто вызывает startActivity(intentForActivityA)
. Если в действии A используется значение по умолчанию android:launchMode="standard"
, то создается новый экземпляр действия, и сохраненное состояние не используется. Есть три способа исправить это, с различными преимуществами и недостатками.
Вариант 1
Переопределите getParentActivityIntent() в действии B и укажите соответствующие дополнительные функции, необходимые для активности A:
@Override
public Intent getParentActivityIntent() {
Intent intent = super.getParentActivityIntent();
intent.putSerializable("CustInfo", m_custInfo);
return intent;
}
Примечание. Если вы используете ActionBarActivity и панель инструментов из библиотеки поддержки, вам необходимо переопределить getSupportParentActivityIntent(), вместо этого.
Я чувствую, что это самое элегантное решение, так как оно работает независимо от того, как пользователь перешел к активности B. Две приведенные ниже опции не работают правильно, если пользователь непосредственно переходит к активности B, не проходя через активность A, для пример, если действие B запускается из обработчика уведомления или URL-адреса. Я думаю, что этот сценарий является причиной того, что API-интерфейс "вверх" является сложным и не просто действует как кнопка "немой".
Один недостаток этого решения заключается в том, что вместо анимации возврата к предыдущему действию используется стандартная анимация перехода к началу нового действия.
Вариант 2
Перехватите кнопку "вверх" и обработайте ее как тупую кнопку "Назад", переопределив onNavigateUp():
@Override
public boolean onNavigateUp() {
onBackPressed();
return true;
}
Примечание. Если вы используете ActionBarActivity и панель инструментов из библиотеки поддержки, вам нужно переопределить onSupportNavigateUp(), вместо этого.
Это решение немного хакерское и работает только для приложений, где "вверх" и "назад" должны вести себя одинаково. Это, вероятно, редко, потому что если "вверх" и "назад" должны вести себя одинаково, то зачем беспокоиться о кнопке "вверх" ? Одно из преимуществ этого решения заключается в том, что используется стандартная анимация возвращения к предыдущей активности.
Вариант 3
Как было предложено yonojoy, установите android:launchMode="singleTop"
на активность A. Подробнее см. его ответ. Обратите внимание, что singleTop
не подходит для всех приложений. Вам придется попробовать и/или потратить некоторое время на чтение документации, чтобы решить для себя.
Ответ 3
Вместо вызова 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 может работать для некоторых простых приложений, но что, если эта активность не была запущена непосредственно из нее родительской? ИЛИ вы можете законно хотеть, чтобы родитель существовал в нескольких местах в фоновом стеке.
Ответ 4
Вы упомянули:
В действии B, когда на панели действий нажата кнопка Up, я хочу вернуться к активности A и быть в том же состоянии, что и раньше.
Если ваш контент в действии A находится во фрагменте, тогда вызовите setRetainInstance (true) в onCreate() фрагмента.
Затем экземпляр фрагмента будет сохранен во время активного отдыха.