Возврат из операции с использованием 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 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 имеет значение null в вашем случае, поскольку ваша активность ранее не была отключена ОС.

Обратите внимание (спасибо, разработчик Android для указания на это ) :

Хотя это решение вопроса, оно не будет работать, если действие, которое возвращается, не находится в верхней части заднего стека. Рассмотрим случай активности A, начинающийся с 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); 

Это происходит потому, что NavUtils.navigateUpFromSameTask() основном просто вызывает startActivity(intentForActivityA) . Если в android:launchMode="standard" A используется android:launchMode="standard" по умолчанию 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 не подходит для всех приложений. Вам придется попробовать и / или потратить некоторое время на чтение документации, чтобы решить для себя.

Вместо вызова 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 может работать для некоторых простых приложений, но что делать, если эта активность не запускалась непосредственно из ее родителя? ИЛИ вы можете законно хотеть, чтобы родитель существовал в нескольких местах в фоновом стеке.

Вы говорите:

В действии B, когда на панели действий нажата кнопка Up, я хочу вернуться к активности A и быть в том же состоянии, что и раньше.

Если ваше содержание в действии A находится во фрагменте, тогда вызовите setRetainInstance (true) в onCreate () фрагмента.

Затем экземпляр фрагмента будет сохранен во время активного отдыха.