Удаление записи из пакета (например, дополнительных) не работает в сочетании с кнопкой "Назад"

У меня есть 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.

Ответ 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, чтобы отразить новое сообщение, вы увидите новое запущенное действие, которое не будет хорошим дизайном пользовательского интерфейса.

Альтернативный подход: Обновите свою базу данных или выполните необходимые действия. Затем локально передайте информацию. В вашей деятельности зарегистрируйте приемник для этой трансляции и обновите пользовательский интерфейс или сделайте все, что необходимо. Если вы хотите уведомить пользователя, а не запускать его, создайте уведомление.