web-dev-qa-db-ja.com

クリアの意図

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

意図をクリアする方法、または以前に使用されたことがあるかどうかを確認する方法はありますか?

94
Mars

更新:

5年前に初めてこの回答を書いたとき、この回答がそれほど言及されているとは思いませんでした!

@ tato-rodrigoの回答によると、これは状況によっては既に処理された意図を検出するのに役立たないことを指摘するために明確にします。

また、私は理由のために引用符で「クリア」を入れたことを指摘する必要があります-あなたはnotこれを行うことで意図を本当にクリアしています、あなたはこの意図のフラグとして余分なものの削除を使用しているだけですすでにアクティビティで見られています。


私はまったく同じ問題を抱えていました。

上記の答えは私を正しい軌道に乗せ、さらに簡単な解決策を見つけました:

getIntent().removeExtra("key"); 

インテントを「クリア」するメソッド呼び出し。

これは1年前に質問されてから少し遅れて回答しましたが、将来的に他の人の助けになることを願っています。

157
Maks

編集:使用している完全なソリューションを投稿するために編集しています。

このソリューションは、問題が「アクティビティが履歴(最近のアプリ)から始まるときに一部のコードを実行しない」の場合に機能します。

まず、booleanActivityを宣言して、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
        }
    }

さらに、ActivitysingleTopに設定されている場合、新しいIntentが配信されたときにフラグをリセットする必要があります。

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

Maksの回答は、余分なものをクリアするために機能します。

    getIntent().removeExtra("key"); 

別の便利なコマンドは次のとおりです。

    getIntent().setAction("");

以下を呼び出すことでインテントにタグを付けることもできます。

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

次に値を確認します。

19
Slynk

Android History(Recent Apps)のアプリ)を起動すると、主に3つの異なるIntentフラグでアプリを起動できます。

  1. _FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY_
    これは、最小化されたアプリの履歴からアクティビティが起動されるときです(ホームキーを長押しします)。
    定数値:1048576(0x00100000)
  2. _FLAG_ACTIVITY_NEW_TASK_
    これは、「アプリケーションアイコンのクリック」または「 Intent filters 」を介してアクティビティが起動されたときです。ここで、アクティビティはこの履歴スタックの新しいタスクの開始になります。
    定数値:268435456(0x10000000)
  3. _FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | FLAG_ACTIVITY_NEW_TASK_
    これは、戻るボタンを押してアプリを終了した後、履歴から再開したときです(最近のアプリ)。
    定数値:269484032(0x10100000)

定数値はgetIntent().getFlags()経由で取得できます

3番目のケースでは、Androidはメモリから最後のIntent値をリロードします。したがって、アプリのインテント(getIntent)は、アプリを起動した最後のインテントの値を持ちます。

実際には、アプリはあたかも新しい起動のように動作する必要があり、前回の起動の意図値ではなく、新しい起動の意図値を使用します。 アプリのアイコンをクリックしてアプリを起動すると、この動作が見られます。古いインテント値はありません。これは、Androidはこのシナリオで次のインテントフィルターを使用するためです

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

ただし、3番目の場合(終了したアプリは、最近のアプリの履歴から起動されます)、Android OSは、(戻るボタンを押して)終了する前にアプリを起動した最後のインテントを使用します。そのため、最終的にはインテント値が古いことになり、アプリのフローは適切ではありません。

意図を削除することはそれを解決する1つの方法ですが、問題を完全に解決することはできません! As Android OSは、起動インテントの最後のインスタンスではなく、アプリの最後の起動からインテントをリロードします。

これを避けるためのクリーンな方法は、タイプ 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パラメーターを使用しないようにする必要があります。

14
B.B.

インテントのクリアオブジェクト

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

短い答えはNo wayです

長い答え。 「ワンショット」インテントのようなものはありません。実験から、最近のAndroidでの最近の活動履歴は「意図の履歴」に過ぎないことが観察されています。アクティビティに渡された最新のインテントは単にシステムにログインされ、それが取引です。上記の人々は使用することを提案します

setAction("")

しかし、onNewIntent()またはonStart()メソッド内でインテントを取得する瞬間までインテントはすでにログに記録されているため、機能しません。

インテントの使用を避けることで問題を解決しました。私の問題は、著者によって投稿された問題に似ていました。通知領域のコントロールを介してアプリケーションからグローバル出口を実装しようとしました。基礎となるサービスを停止し、アプリのすべてのアクティビティを閉じる必要があります。 Wazeアプリケーションでも同じ動作を見つけることができます。

アルゴリズム:

  1. 「Exit」アクションをアクティビティに渡す通知制御用のPendingIntentを作成します。しかし、単純なプロキシである特別なアクティビティに。
  2. プロキシアクティビティonStart()コードはインテントを解析し、アクションをチェックし、一部のモデルの状態を「終了」に設定します。
  3. プロキシアクティビティのonStart()コードは、setIntent( "")を使用して元のインテントをクリアし、startActivity(intent)を呼び出して宛先の「ルート」アクティビティに転送します。
  4. プロキシアクティビティonStart()コードはfinish()を呼び出します。
  5. 宛先アクティビティのonStart()およびonNewIntent()内で、モデルの状態をチェックし、「Exited」の場合はfinish()を呼び出します(私の場合はstopService()も呼び出します)。

インターネットで答えが見つからなかったので、これが誰かの助けになることを願っています。

7
Eugene Wechsler

PendingIntentPendingIntent.FLAG_UPDATE_CURRENTフラグを使用していることを確認してください。

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

ここで、mPutIntentIntentです。

これがお役に立てば幸いです。

4
Hiren Patel

Intent Extraを削除する方法が見つかりませんでしたintentから余分なものを削除することについての答えは、を有効にすると機能しません(Do Not Keep Activities "開発者オプションから(これにより、アクティビティを破棄して、エクストラがまだ存在するかどうかをテストすることができます)。

問題に対する解決策として、Intent Extrasの処理後、ブール値をSharedPreferencesに保存しました。同じインテントがアクティビティに再配信されると、SharedPreferenceの値を確認し、Intent Extraを処理することにします。別の新しい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();
         }
    }

}
1

私はまったく同じ問題を抱えています。私の解決策は、booleanが「使用」されたときに設定されたIntent変数と、このifに基づくbooleanステートメントを追加して、Intentかどうか。

1
pixel

私は最近この問題を抱えていたので、意図に追加パラメーターとしてタイムスタンプを追加することで解決しました。

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

インテントの処理が完了したら、次の操作を行います。

setIntent(null);

処理されたインテントが再び表示されることはなく、処理されたインテントの内容を編集して問題を隠蔽することもありません。

1
straya

解析後に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);
}

このように、元のインテント/インテントエクストラの特定の部分を明示的に保持する機能を保持しながら、元のインテントをダンプするメカニズムがあります。

すべてのアクティビティ起動モードをテストしたわけではないことに注意してください。

0
dell116

簡単な方法は、onCreate()以外のメソッドからgetIntent()を呼び出さないようにすることです。ただし、ユーザーが[ホーム]ボタンをタップしてアクティビティを終了すると、次回の起動時に問題が発生します。この問題には完全に機能する解決策がないと思います。

0
mifthi

同じ問題に直面し、上記の方法を使用しようとしましたが、機能しません。

SingleTopモードを使用したacitvityの起動モードの原因であると思われます。

アプリをバックグラウンドで使用し、RamEaterを使用して、nullを設定したりキーを削除したりしてもインテントに常に余分な問題があることをシミュレートする場合。

この問題は、Androidの設定ストレージを使用して、パススルーしたことを確認することで解消されました。