13

我有一个可以收听传入短信的 BroadcastReceiver。如果消息来自某个发件人,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 中(即 in onCreate()),我smsChallenge从意图中提取值并在提取后使用以下代码删除它:

Bundle extras = getIntent().getExtras();
if (extras != null) {
    smsChallenge = extras.getString("smsChallenge");
    extras.remove("smsChallenge");
}

所以我的应用程序从 SMS 启动并运行良好......但如果我选择按下 BACK 按钮并重新启动应用程序(即通过任务管理器),该值smsChallenge仍在 bundle 中extras。这意味着,我重新启动的应用程序认为它是由于一条不正确的新短信而重新启动的......

任何想法为什么在使用 BACK 按钮并再次重新启动应用程序时从包中删除键值似乎不起作用?

4

7 回答 7

9

因为getExtras()创建了意图附加内容的副本。

你必须这样做

getIntent().removeExtra("smsChallenge");

文档: http: //developer.android.com/reference/android/content/Intent.html#removeExtra(java.lang.String)

于 2014-02-22T08:51:08.157 回答
8

I think that you can not fix this behavior because by the time you get access to the intent, the os would have already saved the intent for later launch via recent apps.

  1. You can either add a flag to remove from recent apps, but in that way the app will not appear in the recent apps.
  2. Add in persistent storage the last sms that was handled in your MainActivity and do this check in your onCreate, discarding an intent for an sms that was already handled.
  3. A final solution would be not to send the data in the intent at all. For example, save them in a "pending" persistent storage. When your MainActivity starts, let it check for pending stuff, and clear this pending item.

Persistent storage could be SharedPreferences for example.

I would advise you to use 2 but you should count the sms received and use that counter as an id for the sms. Do not rely on the sms content and sender as an identifier for the sms.

于 2014-02-21T12:52:29.470 回答
3

“Intent 始终是用于启动 Activity 的原始 Intent,仅此而已。这是不可变的。” 黛安·哈克伯恩

要解决此问题,首先确保在清单中将您的活动 launchMode 声明为 singleTop。

然后从下面的谈话中,尝试使用 onSaveInstanceState 来记录你已经“取消”了一些额外的事实。然后在 onCreate 中,检查它的 'Bundle savedInstanceState' 参数。如果不为null,则为您之前在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();
 }

}

是您问题的另一个有效解决方案

于 2014-02-27T00:07:30.017 回答
1

我认为您需要为您想要的行为明确设置活动的意图。我不确定为什么getIntent()似乎要退回副本。

从文档中Activity.setIntent()

更改 getIntent() 返回的意图。这包含对给定意图的引用;它不会复制它。通常与 onNewIntent(Intent) 结合使用。

http://developer.android.com/reference/android/app/Activity.html#setIntent(android.content.Intent )

于 2012-04-04T17:33:29.603 回答
0

删除多余的不是解决此问题的正确方法。由于用于启动所述活动的上下文而发生此问题。改变这个

final Intent activityIntent = new Intent(context, MainActivity.class);

对此

final Intent activityIntent = new Intent(context.getApplicationContext(), MainActivity.class);

当您按下后退按钮时,活动现在将完成(前提是您没有重载该功能并更改其行为)。如果您从应用程序列表中选择应用程序,它将不包含任何额外内容。

编辑:

避免从接收器启动活动。考虑一下:你在 MainActivity。您收到短信。现在,您将看到一个正在启动的新活动,而不是更新 ui 以反映新消息,这将不是一个好的用户界面设计。

替代方法:更新您的数据库或执行任何需要执行的相关操作。然后在本地广播信息。在您的活动中为此广播注册一个接收器并刷新 UI 或执行任何需要的操作。如果您想通知用户而不是启动活动,请创建通知。

于 2014-02-28T04:28:03.340 回答
0
private void removeExtraFromBundle(String key){
    Bundle bundle = getArguments();
    if(bundle != null){
        String value = null;
        value = bundle.getString(key);
        if(value != null){
            bundle.remove(key);
        }
    }
}

上面的代码将帮助您从捆绑对象中删除特定键。

于 2013-05-14T08:24:25.853 回答
0

我的建议是在意图中添加第三个额外的 some 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);

然后检查你之前是否有这个随机数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()。但是您可以通过检查 if savedInstanceStateis来轻松解决这个问题null

于 2014-02-23T12:14:51.383 回答