Удаление записи из пакета (например, дополнительных) не работает в сочетании с кнопкой "Назад"
У меня есть BroadcastReceiver, который прослушивает входящие SMS. Если сообщение от определенного отправителя, BroadcastReceiver запускает мое приложение со следующим кодом:
final Intent activityIntent = new Intent(context, MainActivity.class);
activityIntent.putExtra("smsChallenge", smsText);
activityIntent.putExtra("smsSenderNumber", senderMobilNumber);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(activityIntent);
В MainActivity моего приложения (т.е. в onCreate()
) я извлекаю значение smsChallenge
из намерения и удаляю его после извлечения следующим кодом:
Bundle extras = getIntent().getExtras();
if (extras != null) {
smsChallenge = extras.getString("smsChallenge");
extras.remove("smsChallenge");
}
Итак, мое приложение запускается из SMS и работает нормально... Но если я нажму кнопку BACK и перезапустил приложение (т.е. через TaskManager), значение smsChallenge
все еще находится в пакете extras
,
Это означает, что мое перезапущенное приложение думает, что оно перезапущено из-за нового SMS, которое не соответствует действительности.
Любые идеи, почему удаление ключевого значения из пакета, похоже, не работает при использовании кнопки BACK и перезапуска приложения снова?
Ответы
Ответ 1
Я думаю, что вы не можете исправить это поведение, потому что к тому времени, когда вы получите доступ к намерению, os уже сохранили намерение для последующего запуска через последние приложения.
- Вы можете добавить флаг для удаления из последних приложений, но таким образом приложение не появится в последних приложениях.
- Добавьте в постоянное хранилище последние sms, которые были обработаны в вашей MainActivity, и выполните эту проверку в вашем onCreate, отбросив намерение для sms, который уже был обработан.
- Окончательным решением было бы не отправлять данные в намерение вообще. Например, сохраните их в "ожидающем" постоянном хранилище. Когда начнется MainActivity, пусть он проверяет наличие ожидающего материала и очищает этот ожидающий элемент.
Постоянное хранилище может быть, например, SharedPreferences.
Я бы посоветовал вам использовать 2, но вы должны посчитать полученные sms и использовать этот счетчик как идентификатор для sms. Не полагайтесь на содержимое sms и отправителя как идентификатор для sms.
Ответ 2
Потому что getExtras()
создает копию дополнительных намерений.
Вы должны сделать так:
getIntent().removeExtra("smsChallenge");
Документы: http://developer.android.com/reference/android/content/Intent.html#removeExtra(java.lang.String)
Ответ 3
"Намерение всегда является первоначальным намерением, используемым для запуска этой деятельности, и ничего более. Это непреложно". Dianne Hackborn
Чтобы обойти проблему, сначала убедитесь, что вы объявили свой запуск launchMode как singleTop в манифесте.
Затем из следующего talk попробуйте использовать onSaveInstanceState для записи факта, что вы "заглушили" некоторые из дополнительных функций.
Затем в onCreate рассмотрите параметр 'Bundle savedInstanceState'. Если он не является нулевым, это предыдущий пакет, который вы вернули в onSaveInstanceState. Из этого Bundle вы можете игнорировать эти дополнения или нет.
Изменить. В вашем случае использование общих настроек позволит вам сохранить состояние, даже если действие будет уничтожено.
public YourActivity extends Activity {
private boolean extrasClearedOut;
private SharedPreferences mPrefs;
private static String EXTRA_CLEAR_OUT = "extras_cleared_out";
public void onCreate(Bundle savedInstanceState) {
mPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
extrasClearedOut = mPrefs.getBoolean(EXTRA_CLEAR_OUT, false);
if (savedInstanceState != null && savedInstanceState.getBoolean(EXTRA_CLEAR_OUT, false)) {
extrasClearedOut = true;
}
Intent intent = getIntent();
if (extrasClearedOut) {
// ignore the extras in the intent.
}
else {
// Read and use the extras in the intent.
}
}
protected void onNewIntent (Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
if (extrasClearedOut) {
// ignore the extras in the intent.
}
else {
// Read and use the extras in the intent.
}
}
public void someMethod(...) {
extrasClearedOut = true;
}
protected void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(EXTRA_CLEAR_OUT, extrasClearedOut);
}
public void onDestroy()
{
final Editor edit = mPrefs.edit();
edit.putBoolean(EXTRA_CLEAR_OUT, extrasClearedOut);
edit.commit();
}
}
Здесь - это еще одно рабочее решение по вашему вопросу
Ответ 4
Я думаю, вам нужно явно указать намерение активности для поведения, которое вы хотите. Я не уверен, почему getIntent()
, похоже, возвращает копию.
Из документов для Activity.setIntent()
Измените намерение, возвращенное getIntent(). Это относится к данному намерению; он не копирует его. Часто используется в сочетании с onNewIntent (Intent).
http://developer.android.com/reference/android/app/Activity.html#setIntent(android.content.Intent)
Ответ 5
private void removeExtraFromBundle(String key){
Bundle bundle = getArguments();
if(bundle != null){
String value = null;
value = bundle.getString(key);
if(value != null){
bundle.remove(key);
}
}
}
Вышеприведенный код поможет вам удалить определенный ключ из объекта bundle.
Ответ 6
Мой совет добавляет третье дополнение к намерению с помощью nonce
, например. просто случайная строка:
final Intent activityIntent = new Intent(context, MainActivity.class);
activityIntent.putExtra("smsChallenge", smsText);
activityIntent.putExtra("smsSenderNumber", senderMobilNumber);
activityIntent.putExtra("nonce", UUID.randomUUID().toString);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(activityIntent);
Затем проверьте, есть ли у вас этот nonce раньше в onCreate()
:
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent = getIntent();
smsChallenge = intent.getStringExtra("smsChallenge", null);
String nonce = intent.getStringExtra("nonce", null);
if (nonce == null) {
// should not happen, but do something sane here
}
SharedPreferences sp = PreferenceManager.getDefaultPreferences(this);
if (nonce.equals(sp.getString("nonce", null)) {
// app was restarted
} else {
// app was started from a shiny fresh intent
sp.edit().putString("nonce", nonce).commit();
}
}
Но имейте в виду, что изменения ориентации и т.д. также вызывают onCreate()
.
Но вы можете легко найти это, проверив, есть ли savedInstanceState
null
.
Ответ 7
Удаление лишнего не является правильным способом решения этой проблемы. Эта проблема возникает из-за контекста, который используется для запуска указанного действия. Измените это
final Intent activityIntent = new Intent(context, MainActivity.class);
к этому
final Intent activityIntent = new Intent(context.getApplicationContext(), MainActivity.class);
действие будет завершено, когда вы нажмете кнопку "Назад" (при условии, что вы не перегрузили функцию и не изменили ее поведение). Если вы выберете приложение из списка приложений, оно не будет содержать никаких дополнительных функций.
Edit:
Избегайте запуска действий с приемника.
Рассмотрим это: вы находитесь на MainActivity. Вы получаете смс. Теперь вместо обновления ui, чтобы отразить новое сообщение, вы увидите новое запущенное действие, которое не будет хорошим дизайном пользовательского интерфейса.
Альтернативный подход:
Обновите свою базу данных или выполните необходимые действия. Затем локально передайте информацию. В вашей деятельности зарегистрируйте приемник для этой трансляции и обновите пользовательский интерфейс или сделайте все, что необходимо. Если вы хотите уведомить пользователя, а не запускать его, создайте уведомление.