清除意图


111

我的Android应用被传递信息的意图(状态栏中的待定意图)调用。

当我按下主屏幕按钮并按住主屏幕按钮重新打开我的应用程序时,它将再次调用该意图,并且相同的附加功能仍然存在。

    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
      super.onSaveInstanceState(savedInstanceState);
    }
    @Override
    public void onRestoreInstanceState(Bundle savedInstanceState) {
      super.onRestoreInstanceState(savedInstanceState);
    }

这是不按预期运行的代码

    String imgUrl;
    Bundle extras = this.getIntent().getExtras();


    if(extras != null){
        imgUrl = extras.getString("imgUrl");
        if( !imgUrl.equals(textView01.getText().toString()) ){

            imageView.setImageDrawable( getImageFromUrl( imgUrl ) );
            layout1.setVisibility(0);
            textView01.setText(imgUrl);//textview to hold the url

        }

    }

我的意图是:

public void showNotification(String ticker, String title, String message, 
    String imgUrl){
    String ns = Context.NOTIFICATION_SERVICE;
    NotificationManager mNotificationManager = 
        (NotificationManager) getSystemService(ns);
    int icon = R.drawable.icon;        // icon from resources
    long when = System.currentTimeMillis();         // notification time
    CharSequence tickerText = ticker;              // ticker-text

    //make intent
    Intent notificationIntent = new Intent(this, activity.class);
    notificationIntent.putExtra("imgUrl", imgUrl);
    notificationIntent.setFlags(
        PendingIntent.FLAG_UPDATE_CURRENT | 
        PendingIntent.FLAG_ONE_SHOT);
    PendingIntent contentIntent = 
        PendingIntent.getActivity(this, 0, 
        notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT | 
        PendingIntent.FLAG_ONE_SHOT);

    //make notification
    Notification notification = new Notification(icon, tickerText, when);
    notification.setLatestEventInfo(this, title, message, contentIntent);
    //flags
    notification.flags = Notification.FLAG_SHOW_LIGHTS | 
        Notification.FLAG_ONGOING_EVENT | 
        Notification.FLAG_ONLY_ALERT_ONCE | 
        Notification.FLAG_AUTO_CANCEL;
    //sounds
    notification.defaults |= Notification.DEFAULT_SOUND;
    //notify
    mNotificationManager.notify(1, notification);
}

有什么方法可以清除意图或检查它是否曾经被使用过?


您可以发布代码吗?
xandy

我将代码添加到了我的问题
火星

无需清除Intent,您可以确定启动类型并相应地处理应用程序流程。仅在针对通知启动而不是从后台获取Extras时才可使用。stackoverflow.com/questions/4116110/clearing-intent/…–
BB

Answers:


168

更新:

当我在五年多以前第一次写这个答案时,我没有意识到这个答案会得到这么多!

我要指出一点,按照@ tato-rodrigo的回答,这在某些情况下不会帮助您检测已经处理过的意图。

我还应该指出,出于某种原因,我在引号中使用了“清除”-您并没有通过这样做真正清除意图,您只是在使用多余的内容作为标志,表明该意图已经被活动看到了。 。


我有完全一样的问题。

上面的答案使我走上了正确的轨道,我找到了更简单的解决方案,请使用:

getIntent().removeExtra("key"); 

方法调用以“清除” Intent。

自从一年前提出此要求以来,它的回答有点晚,但是希望这对以后的其他人有所帮助。


4
removeExtra()方法是否不接受String参数?像这样的getIntent()。removeExtra(“ String”);
tony9099

25
@Maks我可能错了,但我认为在以下情况下这将不起作用:1)通过通知打开活动;2)按返回按钮完成活动;3)通过历史记录(最近的应用程序)重新打开活动。另一种情况是,系统由于缺乏资源而终止了该应用程序(在开发人员选项下启用“请勿保留活动”,然后按Home键,然后从“历史记录”中再次打开活动)。我在下面发布了我正在使用的解决方案。如果您可以对此发表评论,那就太好了。
tato.rodrigo 2014年

2
不幸的是,它对我们不起作用。我们发现,启动一个新的活动,而该活动又会启动初始的活动,会导致OnNewIntent再次以相同的意图触发。
Le-roy Staines 2014年

2
无需清除Intent,您可以确定启动类型并相应地处理应用程序流程。仅在针对通知启动而不是从后台获取Extras时才可使用。stackoverflow.com/questions/4116110/clearing-intent/…–
BB

2
没为我工作。我遇到的问题与@ tato.rodrigo提到的问题相同,如果通过通知或历史记录或他提到的其他原因打开了活动,则无法清除意图,因此在使用意图信息后我要做的就是重设意图,例如现在setIntent(new Intent()),它的工作正常。
Shubhral

43

编辑:我正在编辑发布我正在使用的完整解决方案。

如果问题是“当活动从历史记录(最近的应用程序)开始时不执行某些代码”,则此解决方案将起作用。

首先,boolean在您的中声明一个,Activity以表明该Intent是否已被消耗:

    private boolean consumedIntent;

然后,使用onSaveInstanceStateonCreate方法安全地存储和恢复此值,以处理配置更改以及系统Activity进入后台时可能杀死您的情况。

    private final String SAVED_INSTANCE_STATE_CONSUMED_INTENT = "SAVED_INSTANCE_STATE_CONSUMED_INTENT";

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean(SAVED_INSTANCE_STATE_CONSUMED_INTENT, consumedIntent);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //set content view ...

        if( savedInstanceState != null ) {
            consumedIntent = savedInstanceState.getBoolean(SAVED_INSTANCE_STATE_CONSUMED_INTENT);
        }

        //other initializations
    }

现在,检查是否可以在onResumemethod 下运行代码。

    @Override
    protected void onResume() {
        super.onResume();

        //check if this intent should run your code
        //for example, check the Intent action
        boolean shouldThisIntentTriggerMyCode = [...];
        Intent intent = getIntent();
        boolean launchedFromHistory = intent != null ? (intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0 : false;
        if( !launchedFromHistory && shouldThisIntentTriggerMyCode && !consumedIntent ) {
            consumedIntent = true;
            //execute the code that should be executed if the activity was not launched from history
        }
    }

此外,如果您Activity将设置为singleTop,则应在Intent交付新标志时重置标志。

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        consumedIntent = false;
    }

12
非常感谢!我已经帮助了这段代码,(intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY)因此现在我可以确定活动的开始时间是从历史开始的,而我可以忽略我的其他活动。
Roman Nazarevych 2014年

1
@Lemberg我有同样的问题,我也像您一样解决了问题,如果您使用推送通知中的一些附加功能,则有可能从历史开始您的活动,并浪费您的附加功能并重定向到与您相同的操作推送通知。
launchFromHistory

即使活动被破坏了,我们又从历史记录堆栈中重新打开它,它也能正常工作吗?
user25

大!即使应用程序被破坏,它似乎也能正常工作...但是,请@ tato.rodrigo boolean shouldThisIntentTriggerMyCode = [...];从答案中删除(用于什么用途?)
user25

在我的情况多通知特定的用户,最好的用户consumedIntentString含通知UID。该Uid可以简单地作为当前时间戳添加到后端的通知中。另外,onSaveInstanceState只有在有Intent出现时,才应保存此Uid onCreate。这意味着您不应该从中保存Uid onNewIntent
Konstantin Konopko

22

Maks回答可以清除额外的内容:

    getIntent().removeExtra("key"); 

另一个有用的命令是:

    getIntent().setAction("");

您还可以通过以下方式标记意图:

    getIntent().putExtra("used", true);

然后只需检查该值。


21

当我们从“历史记录”(最近的应用程序)中启动Android应用程序时,该应用程序可能主要使用三个不同的Intent标志启动。

  1. FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
    这是从最小化的应用程序历史记录(长按主键)启动活动时。
    常数值:1048576(0x00100000)
  2. FLAG_ACTIVITY_NEW_TASK
    通过“点击应用程序图标”或“ 意图过滤器 ”启动活动时。在这里,活动将成为此历史记录堆栈上新任务的开始。
    常数:268435456(0x10000000)
  3. FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | FLAG_ACTIVITY_NEW_TASK
    这是通过按后退按钮退出应用程序,然后从“历史记录”(最近的应用程序)中恢复的情况。
    常数值:269484032(0x10100000)

常数值可以通过以下方式获取 getIntent().getFlags()

在第三种情况下,Android从其内存中重新加载最后一个Intent值。因此,您应用程序的意图(getIntent)将具有启动该应用程序的最后一个意图的值。

实际上,该应用程序的行为应像是新启动一样,具有新启动的意图值,而不是先前启动的意图值。如果您通过单击应用程序图标启动应用程序,则可以看到此行为,它将永远不会有旧的意图值。这是因为,对于这种情况,Android使用以下意图过滤器

 <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER"/>
 </intent-filter>

但是在第三种情况下(退出的应用程序是从“最近的应用程序的历史记录”中启动的),Android OS使用退出该应用程序之前(通过按“后退”按钮)启动该应用程序的最后意图。因此,您最终会拥有旧的Intent值,并且应用程序流程不正确。

消除意图是解决它的一种方法,但不能完全解决问题!随着Android操作系统从上次启动而不是最后一次启动意图实例中重新加载应用程序的意图。

避免发生这种情况的一种干净方法是通过获取Intent类型来确定启动类型来对其进行处理。

因此,在您LaunchActivity(这在清单中定义的意图过滤器中的一个),你可以在使用下面的代码onCreate()onStart()onResume()方法。

if(getIntent().getFlags() == (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY)) {
    //app is launched from recent apps after it was closed
        normalLaunch();
    } else {
        String intentAction = getIntent().getAction();
        String scheme = getIntent().getScheme();
        //app is launched via other means
        // URL intent scheme, Intent action etc
        if("https".equalsIgnoreCase(scheme)) {
            // URL intent for browser
        } else if("com.example.bb".equalsIgnoreCase(intentAction)) {
            // App launched via package name
        } else {
            // App was launched via Click on App Icon, or other means
            normalLaunch();
        }
    }

我认为normalLaunch(),不应使用Intent中的参数;否则,您将需要隔离和优化默认的启动方法以不使用Intent参数。


1
并非所有英雄都戴帽子!
Sdghasemi

我不知道,但它总是返回true getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY并不重要,无论我是从另一个活动(startActivity方法)开始活动还是从“历史记录”堆栈(最近的应用程序)中重新打开它。
user25

您将不得不将其与其他标志结合使用,例如FLAG_ACTIVITY_NEW_TASK
BB

1
当活动由于开发人员设置“不保留活动”而在后台被杀死时,此方法将不起作用。在这种情况下,getIntent()getFlags()与活动第一次启动时相同。
Malachiasz

欣赏的解释,但它并没有如预期
阿兹兰贾马尔-

18

清除意图对象

intent.replaceExtras(new Bundle());
intent.setAction("");
intent.setData(null);
intent.setFlags(0);

2
这应该是公认的答案。真的很好!
马丁·埃里奇

2
在开发人员选项中选中“不保留活动”时不起作用
Jemshit Iskenderov

8

简短的答案是没有办法

长答案。没有“一次性”的意图。从实验中可以看出,现代Android的近期活动历史仅是“意图历史”。传递给活动的最新意图只需登录到系统即可。上面的人建议使用

setAction("")

但这是行不通的,因为该意图已经记录,直到您在onNewIntent()或onStart()方法中获取它为止。

我通过避免使用意图解决了这个问题。我的问题与作者所发表的问题相似。我试图通过通知区域中的控件从应用程序实现全局退出。它应停止基础服务并关闭该应用程序的所有活动。您可以在位智应用程序中找到相同的行为。

算法:

  1. 为通知控件创建PendingIntent,该控件将“退出”操作传递给活动。但是对于特殊的活动,这是一个简单的代理。
  2. 代理活动的onStart()代码解析意图,检查动作并将某些模型的状态设置为“已退出”。
  3. 代理活动的onStart()代码使用setIntent(“”)清除原始意图,然后通过调用startActivity(intent)将其转发到目标“ Root”活动。
  4. 代理活动onStart()代码调用finish()。
  5. 在目标活动的onStart()和onNewIntent()内部,检查模型状态,并在其为“ Exited”的情况下调用finish()(在本例中,还请调用stopService())。

我希望这对某人有帮助,因为我没有在互联网上找到答案。


我认为这是最准确的答案,因为“清除意图”并不一定意味着“删除某些多余的内容”。我也认为没有简单的方法可以做到这一点。
mdelolmo 2014年

5

确保您对PendingIntent使用PendingIntent.FLAG_UPDATE_CURRENT标志。

PendingIntent pendingIntent = PendingIntent.getActivity(this, 100, mPutIntent, PendingIntent.FLAG_UPDATE_CURRENT);

哪里mPutIntent是你的Intent

希望这会帮助你。


1
救了我一命!!
eren130 '16

1
我不明白这不是公认的答案。我唯一的遗憾只有一个:只有一次投票。干杯。
安迪

2

我最近遇到了这个问题,并通过添加时间戳作为意图的额外参数来解决了这个问题:

private void launchActivity(Context context) {
    Intent intent = new Intent(context, MainActivity.class);
    intent.putExtra("KEY_EXTRA_TIMESTAMP", System.currentTimeMillis());
    context.startActivity(intent);
}

之后,将时间戳保存到共享首选项:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    long time = getIntent().getLongExtra("KEY_EXTRA_TIMESTAMP", -1);
    long previousTime = getPreferences(MODE_PRIVATE).getLong("timestamp", -1);

    //ignore if the timestamp is the same as the previous one  
    if (time != previousTime) {
        handleIntent(getIntent());
        if (time != -1) {
            //save the timestamp
            getPreferences(MODE_PRIVATE).edit().putLong("timestamp", time).apply();
        }
    }
}

1

我有完全一样的问题。我的解决方案是添加booleanIntent“使用” 时设置的变量,并if基于此声明boolean以检查是否应使用Intent


3
这可能不起作用,因为在活动停止然后重新启动时,将重新创建布尔值(如果在活动中声明为全局值)。(例如,单击“主页”按钮)
tony9099 2013年

1

处理完意图后,请执行以下操作:

setIntent(null);

您将不会再看到已处理的Intent,也不会通过编辑已处理的Intent的内容来掩盖问题。


1

我找不到删除Intent Extra的方法。如果从开发人员选项中启用“请勿保留活动 ”,那么从意图中删除多余内容的任何方法都无法解决(这样,您就可以销毁Activity并重新测试是否还存在Extras)。

作为解决该问题,我存储在布尔值SharedPreferences意图附加处理之后。当将相同的Intent重新交付给Activity时,我检查SharedPreference值并决定处理Extra Intent。如果您向同一活动发送另一个新的Intent Extra,则将SharedPreference值设置为false,活动将对其进行处理。范例

// Start Activity with Intent Extras
Intent intent = new Intent(context, MyActivity.class);
intent.putExtra("someData", "my Data");
// Set data as not processed
context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE).edit().putBoolean("myActivityExtraProccessed", false).commit();
context.startActivity(intent);

...

public class MyActivity{

    ...
    public void someMethod(){
        boolean isExtrasProcessed = context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE).getBoolean("myActivityExtraProccessed", false);  
         if (!isExtrasProcessed) {
              // Use Extras

              //Set data as processed
              context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE).edit().putBoolean("myActivityExtraProccessed", true).commit();
         }
    }

}

偏好没有意义,你不知道,如果你使用startActivity或者你从历史堆栈重新打开它开始活动...
user25

@ user25我认为有一种方法可以检测它是否从最近的应用程序启动。但这很重要,意图额外是否被消耗了,如果您从共享偏好中知道了意图是否被消耗了。我已经用它来额外消耗推送通知,并且对于我的案例而言,如何打开活动都没有关系。
Jemshit Iskenderov '18

0

即使在解析完Intent和Intent附加信息后手动清除它们,似乎Activity.getIntent()始终会返回启动该Activity的原始Intent。

为了解决这个问题,我推荐这样的事情:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // The Intent provided by getIntent() (and its extras) will persist through a restore
    // via savedInstance.  Because of this, restoring this activity from a
    // an instance that was originally started with extras (deep-link or 
    // pre-defined destination) may cause un-desired behavior
    // (ie...infinite loop of sending the user directly to somewhere else because of a
    // pre-defined alternate destination in the Intent's extras).
    //
    // To get around this, if restoring from savedInstanceState, we explicitly
    // set a new Intent *** to override the original Intent that started the activity.***
    // Note...it is still possible to re-use the original Intent values...simply
    // set them in the savedInstanceState Bundle in onSavedInstanceState.
    if (savedInstanceState != null) {
        // Place savedInstanceState Bundle as the Intent "extras"
        setIntent(new Intent().putExtras(savedInstanceState));
    }

    processIntent(getIntent())
}

private void processIntent(Intent intent) {
    if (getIntent().getExtras() == null) {
        // Protection condition
        return;
    }

    doSomething(intent.getExtras.getString("SOMETHING_I_REALLY_NEED_TO_PERSIST"));

    final String somethingIDontWantToPersist = 
        intent.getExtras.getString("SOMETHING_I_DONT_WANT_TO_PERSIST");

    if(somethingIDontWantToPersist != null) {
        doSomething(somethingIDontWantToPersist);
    }
}

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save selective extras from original Intent...
    savedInstanceState.putString("SOMETHING_I_REALLY_NEED_TO_PERSIST", "persistedValued");
    super.onSaveInstanceState(savedInstanceState);
}

这样,有一种机制可以转储原始Intent,同时仍然保留显式保留原始Intent / Intent附加内容的某些部分的功能。

请注意,我尚未测试所有Activity启动模式。


0

直接方法是避免从onCreate()以外的方法调用getIntent()。但是,如果用户通过单击“主页”按钮离开了我们的活动,这将在下次启动时引起问题。我认为这个问题没有完整的功能解决方案。


0

我遇到同样的问题,并尝试使用上述方法,但不起作用。

我认为这可能是我使用singleTop模式导致活动启动模式的原因。

当我后台应用程序并使用RamEater模拟问题时,即使将其设置为null或删除键,该意图也总是具有额外的意义。

通过在Android上使用首选项存储来检查是否已通过,该问题已解决。


0

为了知道这些额外功能是否已被消耗,添加另一个额外功能不是一个好习惯,为什么不这样做呢?:

if (intent.hasExtra(EXTRA_NAME) && intent.getBooleanExtra(EXTRA_NAME, false)) {
    // consume extra here after that set it to false
    putExtra(EXTRA_NAME, false)
}   

-1

这个怎么样?将newIntent设置为意图。

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    setIntent(intent);
}

1
我认为那行不通。从历史记录重新创建活动后,意图不会受到影响。
mdelolmo

-1

当您希望清除意图时,将其替换为空,该怎么办?

例如。

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    setIntent(intent);
}

@Override 
public void onResume() {
    super.onResume();

    Intent theIntent = getIntent();
    if ("myaction".equals(theIntent.getAction()) {
         handleIntent();
         onNewIntent(new Intent());  // <--- "clear" the intent by setting empty one
    }
}

-1

希望对大家有帮助。所以首先我们要得到意图

//globally
Intent myIntent;

将其放在创建的某个位置

myIntent = getIntent();
String data = myIntent.getStringExtra("yourdata");
//Your process here

现在让我们进行设置,以便每当我们的应用程序被破坏或退出时,我们都将删除数据

@Override
protected void onDestroy() {
    //TODO: Clear intents
    super.onDestroy();
    myIntent.removeExtra("data");
}
@Override
protected void onBackPressed() {
    //TODO: Clear intents
    super.onBackPressed();
    myIntent.removeExtra("data");
}

您知道了,如果还不够,那就找到更多的“ on”回调


仅当您退出应用程序时,意图才会被清除。刷最近的东西。
Pixeldroid Modding

-2

虽然Intent.removeExtra("key")会从附加功能中删除一个特定的键,但是还有方法Intent.replaceExtras(Bundle),如果null作为参数传递,则可用于从Intent中删除整个附加功能。

从文档:

用给定的附加组件包完全替换Intent中的附加组件。

参数
extras Intent中新的Extras集,或者为null以擦除所有Extras。

因为putXXX()方法使用空的Bundle(如果为null)来初始化附加项,所以这没有问题。


-3
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
intent.addCategory(Intent.CATEGORY_HOME);  
startActivity(intent);

2
您应该考虑通过提供一些详细信息来详细说明您的答案,这是您在做什么以及它如何回答OP的问题。
forsvarir 2012年

1
@Ramkumar,将用户带回家。很明显,这只是10000个案例中的一个案例,可能会导致调用onPause,onStop或onDestroy。
tony9099

这甚至与问题无关
Hossein Shahdoost
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.