Учет целей

Мое Android-приложение получает вызов по намерению, передающему информацию (pendingintent в строке состояния).

Когда я нажимаю кнопку "домой" и снова открываю приложение, удерживая кнопку "домой", он снова называет намерение, и те же дополнительные функции все еще существуют.

    @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);
}

Есть ли способ очистить намерение или проверить, использовалось ли оно раньше?

Ответ 1

UPDATE:

Я не понимал, что этот ответ будет упомянут так, когда я впервые написал его более 5 лет назад!

Я поясню, что в соответствии с ответом @tato-rodrigo это не поможет вам обнаружить уже обработанное намерение в некоторых ситуациях.

Также я должен указать, что я поместил "ясно" в кавычки по какой-то причине - вы не действительно очищаете намерение, делая это, вы просто используете удаление лишнего в качестве флага что это намерение уже проявилось в активности.


У меня была точно такая же проблема.

Выше ответ поставил меня на правильный трек, и я нашел еще более простое решение, используя:

getIntent().removeExtra("key"); 

вызов метода "очистить" намерение.

Его немного поздно ответить, так как это было задано год назад, но, надеюсь, это поможет другим в будущем.

Ответ 2

EDIT: Я редактирую, чтобы опубликовать полное решение, которое я использую.

Это решение будет работать, если проблема: "Не выполнять какой-либо код, когда действие начинается с" История (последние приложения) ".

Прежде всего, объявите boolean в Activity, чтобы указать, был ли уже использован Intent:

    private boolean consumedIntent;

Затем безопасно храните и восстанавливайте это значение с помощью методов onSaveInstanceState и onCreate для обработки изменений конфигурации и случаев, когда система может убить ваш 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
    }

Теперь проверьте, можете ли вы запустить свой код под onResume.

    @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, вы должны reset ваш флаг, когда будет доставлен новый Intent.

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

Ответ 3

Макс отвечает за очистку:

    getIntent().removeExtra("key"); 

Еще одна полезная команда:

    getIntent().setAction("");

Вы также можете пометить намерение, вызвав:

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

а затем просто проверьте значение.

Ответ 4

Когда мы запускаем Android-приложения из истории (последние приложения), приложение может быть запущено с использованием в основном трех разных флагов Intent.

  • FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
    Это когда активность запускается из истории приложения, которое было сведено к минимуму (долгое нажатие домашнего ключа).
    Постоянное значение: 1048576 (0x00100000)
  • FLAG_ACTIVITY_NEW_TASK
    Это когда активность запускается через "щелчок значка приложения" или через " Intent filters". Здесь действие станет началом новой задачи в этом стеке истории.
    Постоянное значение: 268435456 (0x10000000)
  • 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 использует последнее намерение, которое запустило приложение до его выхода (с помощью кнопки "Назад" ). Таким образом, вы получаете старые значения намерений, а поток приложений не является правильным.

Удаление намерения - это один из способов его решения, но это не решит проблему полностью! Поскольку ОС Android перезагружает Intent из последнего запуска приложений, а не последнего экземпляра намерения запуска.

Чистый способ избежать этого - это обработать его, получив тип 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.

Ответ 5

Очистка объекта намерения:

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

Ответ 6

Короткий ответ Нет способа

Длинный ответ. Нет такого понятия, как "одноразовое" намерение. Из эксперимента видно, что недавняя история активности в современных андроидах - не что иное, как "Намеренная история". Последнее намерение, переданное в действие, просто регистрируется в системе и что сделка. Люди, предлагающие использовать

setAction("")

Но это не работает, потому что намерение уже зарегистрировано до момента его получения внутри метода onNewIntent() или onStart().

Я решил проблему, избегая использования намерений. Моя проблема была знакома с тем, который был опубликован автором. Я попытался реализовать Global Exit из приложения через управление в области уведомлений. Он должен остановить базовую услугу и закрыть все действия приложения. Вы можете найти такое же поведение в приложении Waze.

Алгоритм:

  • Создать PendingIntent для управления уведомлением, который передает действие "Выход" в действие. Но к специальной деятельности, которая является простым прокси.
  • Прокси-активность onStart() кода анализирует намерение, проверяет действие и устанавливает состояние какой-либо модели на "Выход" .
  • Прокси-активность: код onStart() очищает исходное намерение с помощью setIntent ( "), а затем перенаправляет его в пункт назначения" Root", вызывая startActivity (намерение).
  • Активность прокси-сервера onStart() вызывает invoke finish().
  • Внутри onStart() и onNewIntent() состояния проверки состояния цели и завершения вызова(), если это "Выход" (а также вызов stopService() в моем случае).

Надеюсь, это поможет кому-то, потому что я не нашел ответа в Интернете.

Ответ 7

Убедитесь, что для PendingIntent используется PendingIntent.FLAG_UPDATE_CURRENT..

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

Где mPutIntent - ваш Intent.

Надеюсь, это поможет вам.

Ответ 8

У меня точно такая же проблема. Моим решением было добавить переменную boolean, которая была установлена, когда Intent был "использован" и if на основе этого boolean, чтобы проверить, следует ли использовать Intent или нет.

Ответ 9

Когда вы закончите обработку намерения, сделайте следующее:

setIntent(null);

Вы не увидите этот обработанный Intent снова, и вы не будете маскировать проблему, отредактировав содержимое обработанного Intent.

Ответ 10

У меня недавно была эта проблема, и я решил ее, добавив временную метку в качестве дополнительного параметра для намерения:

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();
        }
    }
}

Ответ 11

Я не смог найти способ удалить Intent Extra. Ни один из ответов об удалении дополнительных из намерений не работает, если вы включите "Не выполнять действия" из параметров разработчика (таким образом вы можете уничтожить Activity и вернуться к проверке, если Extras все еще существует).

В качестве решения к проблеме я сохранил логическое значение в SharedPreferences после обработки Intent Extras. Когда тот же Intent повторно добавлен в Activity, я проверяю значение SharedPreference и решает обработать Intent Extra. В случае, если вы отправляете еще один новый Intent Extra в ту же самую активность, вы делаете SharedPreference значение false, и Activity будет обрабатывать его. Пример:

// 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();
         }
    }

}

Ответ 12

Этот способ работал со мной:

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

Ответ 13

Даже после ручной очистки настроек Intent и Intent после их анализа кажется, что Activity.getIntent() всегда возвращает исходное намерение, которое запустило Activity.

Чтобы обойти это, я рекомендую что-то вроде этого:

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 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.

Обратите внимание, что я не тестировал все режимы запуска Activity.

Ответ 14

Прямой путь заключается в том, чтобы избежать вызова getIntent() из методов, отличных от onCreate(). Но это вызовет проблему во время следующего запуска, если пользователь оставит нашу активность, нажав кнопку "Домой". Я думаю, что эта проблема не имеет полностью функционального решения.

Ответ 15

Я сталкиваюсь с той же проблемой и пытаюсь использовать вышеописанные методы, но она не работает.

Я думаю, что это может быть причиной запуска режима активности, который я использовал режим SingleTop.

Когда я создаю фоновое приложение и использую RamEater для имитации проблемы, у намерения всегда есть дополнительные, даже если я установил его в ноль или удалил ключ.

Проблема устранена путем использования предпочтительного хранилища на Android для проверки того, что прошло.

Ответ 16

Как насчет этого? Устанавливает newIntent как намерение.

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

Ответ 17

Как насчет того, когда вы хотите очистить намерение - замените его на пустой?

например.

@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
    }
}

Ответ 18

Это, надеюсь, поможет всем. Итак, сначала мы получаем намерение

//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' обратные вызовы

Ответ 19

В то время как Intent.removeExtra("key") будет удалять один специальный ключ из дополнительных функций, существует также метод Intent.replaceExtras(Bundle), который может используется для удаления всех дополнительных объектов из Intent, если null передается как параметр.

Из документов:

Полностью замените дополнительные функции в намерении данным комплектом статистов.

Параметры
Дополнительные параметры Новый набор дополнительных функций в Intent, или null, чтобы удалить все дополнительные функции.

Поскольку методы putXXX() инициализируют дополнительные компоненты новым светом Bundle, если его значение равно null, это не проблема.

Ответ 20

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