242

アプリケーションのすべてのアクティビティを表示するには、ユーザーがログインする必要があります。ユーザーは、ほぼすべてのアクティビティからログアウトできます。これはアプリケーションの要件です。ユーザーがログアウトした場合はいつでも、ユーザーを Login に送信したいと考えていますActivity。この時点で、このアクティビティを履歴スタックの一番下に配置して、「戻る」ボタンを押すとユーザーが Android のホーム画面に戻るようにします。

この質問がいくつかの異なる場所に尋ねられたのを見てきましたが、すべて同様の回答が返されました (ここで概説します) が、フィードバックを収集するためにここに提示したいと思います。

Intentドキュメントに記載されているようにフラグを設定して Login アクティビティを開こうとしましたFLAG_ACTIVITY_CLEAR_TOPが、Login アクティビティを履歴スタックの一番下に配置し、ユーザーが戻るのを防ぐという目標を達成できませんでした以前に見たログイン アクティビティに。マニフェストの Login アクティビティにも使用android:launchMode="singleTop"してみましたが、これも私の目標を達成していません (とにかく効果がないようです)。

履歴スタックをクリアするか、以前に開いたアクティビティをすべて終了する必要があると思います。

1 つのオプションは、各アクティビティのonCreateログイン状態をチェックし、ログインしfinish()ていない場合はチェックすることです。このオプションは好きではありません。戻るボタンは引き続き使用でき、アクティビティが閉じると元に戻るからです。

次のオプションは、どこからでも静的にアクセスできるすべての開いているアクティビティへの参照を維持するLinkedListことです (おそらく弱参照を使用します)。ログアウトすると、このリストにアクセスして、以前に開いたすべてのアクティビティを反復処理し、finish()それぞれを呼び出します。私はおそらくすぐにこの方法を実装し始めるでしょう。

Intentただし、これを達成するには、フラグの策略を使用したいと思います。上で概説した 2 つの方法のいずれかを使用しなくても、アプリケーションの要件を満たすことができれば幸いです。

Intentまたはマニフェスト設定を使用してこれを達成する方法はありますか、または私の2番目のオプションはLinkedList、開いているアクティビティを維持するのが最良のオプションですか? または、私が完全に見落としている別のオプションはありますか?

4

18 に答える 18

216

私見より堅牢な別のアプローチをお勧めします。基本的に、ログイン状態を維持する必要があるすべてのアクティビティにログアウト メッセージをブロードキャストする必要があります。したがって、を使用して、すべてのアクティビティにsendBroadcastインストールできます。BroadcastReceiverこのようなもの:

/** on your logout method:**/
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("com.package.ACTION_LOGOUT");
sendBroadcast(broadcastIntent);

レシーバー (保護されたアクティビティ):

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    /**snip **/
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("com.package.ACTION_LOGOUT");
    registerReceiver(new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d("onReceive","Logout in progress");
            //At this point you should start the login activity and finish this one
            finish();
        }
    }, intentFilter);
    //** snip **//
}
于 2010-06-09T18:24:44.013 に答える
152

新しい Android プログラマーがこの問題を調査し、これらの StackOverflow スレッドをすべて読むのに 1 日を費やすのは通過儀礼のようです。私は今、新たに秘儀参入を受けましたが、将来の巡礼者を助けるために、謙虚な経験の痕跡をここに残します。

まず、私の調査によると、これを行うための明白または即時の方法はあり(as of September 2012).ませstartActivity(new Intent(this, LoginActivity.class), CLEAR_STACK)ん。

これにより、フレームワークがスタックを下方向に検索し、LoginActivity の以前の元のインスタンスを見つけ、それを再作成し、残りの (上向きの) スタックをクリアしstartActivity(new Intent(this, LoginActivity.class))ます。FLAG_ACTIVITY_CLEAR_TOPLogin はおそらくスタックの一番下にあるため、スタックは空になり、[戻る] ボタンをクリックするとアプリケーションが終了します。

しかし、これは、LoginActivity の元のインスタンスをスタックのベースで有効にしたままにしておいた場合にのみ機能します。多くのプログラマーのように、ユーザーが正常にログインした後にそれを選択しfinish()LoginActivity場合、それはもはやスタックのベースではなく、FLAG_ACTIVITY_CLEAR_TOPセマンティクスは適用されません...LoginActivity既存のスタックの上に新しいものを作成することになります。これはほぼ間違いなく、あなたが望むものではありません (ユーザーがログインから前の画面に「戻る」ことができるという奇妙な動作)。

finish()そのためLoginActivity、以前にLoginActivity. このスレッドでの答え@doreamonが最良の解決策のようです(少なくとも私の謙虚な目には):

https://stackoverflow.com/a/9580057/614880

LoginActivity を有効にしておくかどうかの複雑な意味が、この混乱の多くを引き起こしているのではないかと強く思います。

幸運を。

于 2012-09-17T23:06:54.327 に答える
122

アップデート

superfinishAffinity()メソッドはコードを削減するのに役立ちますが、同じことを達成します。現在のアクティビティとスタック内のすべてのアクティビティを終了しますgetActivity().finishAffinity()。フラグメントにいる場合に使用します。

finishAffinity(); 
startActivity(new Intent(mActivity, LoginActivity.class));

元の答え

LoginActivity --> HomeActivity --> ... --> SettingsActivity 呼び出し signOut():

void signOut() {
    Intent intent = new Intent(this, HomeActivity.class);
    intent.putExtra("finish", true);
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // To clean up all activities
    startActivity(intent);
    finish();
}

ホームアクティビティ:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    boolean finish = getIntent().getBooleanExtra("finish", false);
    if (finish) {
        startActivity(new Intent(mContext, LoginActivity.class));
        finish();
        return;
    }
    initializeView();
}

これは私にとってはうまくいきます。あなたにとっても役立つことを願っています。:)

于 2012-03-06T08:16:06.757 に答える
75

API 11以降を使用している場合は、これを試すことができます: FLAG_ACTIVITY_CLEAR_TASK--それはあなたが抱えている問題に正確に対処しているようです. 明らかに、API 11 より前のクラウドは、@doreamon が示唆するように、すべてのアクティビティを追加でチェックするか、その他の策略を組み合わせて使用​​する必要があります。

(注意: これを使用するには、 を渡す必要がありますFLAG_ACTIVITY_NEW_TASK)

Intent intent = new Intent(this, LoginActivity.class);
intent.putExtra("finish", true); // if you are checking for this in your other Activities
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | 
                Intent.FLAG_ACTIVITY_CLEAR_TASK |
                Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
于 2013-02-04T22:54:47.147 に答える
32

私もこれに数時間を費やしました...そしてFLAG_ACTIVITY_CLEAR_TOPがあなたが望むもののように聞こえることに同意します:起動中のアクティビティを除いてスタック全体をクリアし、[戻る]ボタンでアプリケーションを終了します。しかし、Mike Repassが述べたように、FLAG_ACTIVITY_CLEAR_TOP は、起動しているアクティビティが既にスタックにある場合にのみ機能します。アクティビティが存在しない場合、フラグは何もしません。

何をすべきか?FLAG_ACTIVITY_NEW_TASKを使用して、起動中のアクティビティをスタックに配置します。これにより、そのアクティビティが履歴スタックの新しいタスクの開始になります。次に、FLAG_ACTIVITY_CLEAR_TOP フラグを追加します。

これで、FLAG_ACTIVITY_CLEAR_TOP がスタック内の新しいアクティビティを見つけようとすると、そこにあり、他のすべてがクリアされる前にプルアップされます。

これが私のログアウト機能です。View パラメーターは、関数が関連付けられているボタンです。

public void onLogoutClick(final View view) {
    Intent i = new Intent(this, Splash.class);
    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    startActivity(i);
    finish();
}
于 2013-06-19T14:15:06.867 に答える
17

たくさんの答え。これも役立つかもしれません-

Intent intent = new Intent(activity, SignInActivity.class)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent);
this.finish();

Kotlin バージョン -

Intent(this, SignInActivity::class.java).apply {
    addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
    addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}.also { startActivity(it) }
finish()
于 2015-07-22T09:38:41.770 に答える
4

これを使用すると、役立つはずです。少し変更された xbakesx の回答。

Intent intent = new Intent(this, LoginActivity.class);
if(Build.VERSION.SDK_INT >= 11) {
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK);
} else {
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
}
startActivity(intent);
于 2014-06-25T08:37:45.467 に答える
4

承認された解決策は正しくありません。ブロードキャスト レシーバーを使用することはこの問題には適していないため、問題があります。アクティビティが既に onDestroy() メソッドを呼び出している場合、レシーバーは取得されません。最善の解決策は、共有設定にブール値を設定し、アクティビティの onCreate() メソッドで確認することです。ユーザーがログインしていないときに呼び出す必要がない場合は、アクティビティを終了します。そのためのサンプルコードを次に示します。とてもシンプルで、あらゆる条件に対応します。

protected void onResume() {
  super.onResume();
  if (isAuthRequired()) {
    checkAuthStatus();
  }
}

private void checkAuthStatus() {
  //check your shared pref value for login in this method
  if (checkIfSharedPrefLoginValueIsTrue()) {
    finish();
  }
}

boolean isAuthRequired() {
  return true;
}
于 2015-07-09T16:32:55.093 に答える
3

これが私のアプリで思いついた解決策です。

LoginActivity では、ログインを正常に処理した後、API レベルに応じて次のログインを別の方法で開始します。

Intent i = new Intent(this, MainActivity.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    startActivity(i);
    finish();
} else {
    startActivityForResult(i, REQUEST_LOGIN_GINGERBREAD);
}

次に、LoginActivity の onActivityForResult メソッドで:

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB &&
        requestCode == REQUEST_LOGIN_GINGERBREAD &&
        resultCode == Activity.RESULT_CANCELED) {
    moveTaskToBack(true);
}

最後に、他のアクティビティでログアウトを処理した後:

Intent i = new Intent(this, LoginActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);

Gingerbread の場合、MainActivity から戻るボタンを押すと、LoginActivity がすぐに非表示になります。Honeycomb 以降では、ログインの処理後に LoginActivity を終了するだけで、ログアウトの処理後に適切に再作成されます。

于 2013-11-04T22:56:30.123 に答える
3

この質問には別のアプローチをお勧めします。おそらく最も効率的なものではないかもしれませんが、適用するのが最も簡単で、必要なコードはほとんどないと思います。最初のアクティビティ (私の場合はログイン アクティビティ) に次のコードを記述しても、ユーザーはログアウト後に以前に起動したアクティビティに戻ることができません。

@Override
public void onBackPressed() {
    // disable going back to the MainActivity
    moveTaskToBack(true);
}

ユーザーがログインした直後に LoginActivity が終了すると想定しているため、後で戻るボタンを押して戻ることはできません。代わりに、適切にログアウトするには、ユーザーはアプリ内のログアウト ボタンを押す必要があります。このログアウト ボタンが実装するのは、次のような単純なインテントです。

Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
finish();

すべての提案を歓迎します。

于 2018-02-12T12:07:14.897 に答える
2

選択された答えは巧妙でトリッキーです。これが私がそれをした方法です:

LoginActivity はタスクのルート アクティビティです。Manifest.xml でandroid:noHistory="true"を設定します。たとえば、SettingsActivity からログアウトするには、次のようにします。

    Intent i = new Intent(SettingsActivity.this, LoginActivity.class);
    i.addFlags(IntentCompat.FLAG_ACTIVITY_CLEAR_TASK
            | Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(i);
于 2014-12-18T13:19:14.647 に答える
1

これは私のために働いた:

     // After logout redirect user to Loing Activity
    Intent i = new Intent(_context, MainActivity.class);
    // Closing all the Activities
    i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);

    // Add new Flag to start new Activity
    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

    // Staring Login Activity
    _context.startActivity(i);
于 2015-06-24T12:14:29.443 に答える
0

StartActivityForResult でアクティビティを開始し、ログアウト中に結果を設定し、結果に従ってアクティビティを終了します

intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivityForResult(intent, BACK_SCREEN);

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
    case BACK_SCREEN:
        if (resultCode == REFRESH) {
            setResult(REFRESH);
            finish();
        }
        break;
    }
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        AlertDialog alertDialog = builder.create();

        alertDialog
                .setTitle((String) getResources().getText(R.string.home));
        alertDialog.setMessage((String) getResources().getText(
                R.string.gotoHome));
        alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Yes",
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog,
                            int whichButton) {

                        setResult(REFRESH);
                        finish();
                    }

                });

        alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "No",
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog,
                            int whichButton) {
                    }
                });
        alertDialog.show();
        return true;
    } else
        return super.onKeyDown(keyCode, event);

}
于 2010-12-15T12:50:34.487 に答える
0

SharedPreferences または Application Activity でフラグを管理することで可能です。

アプリの起動時に (スプラッシュ スクリーンで)、フラグ = false を設定します。ログアウト クリック イベントでフラグを true に設定し、すべてのアクティビティの OnResume() でフラグが true かどうかを確認してから、finish() を呼び出します。

それは魅力のように機能します:)

于 2014-07-31T12:47:49.377 に答える
0

ログアウトをクリックすると、これを呼び出すことができます

private void GoToPreviousActivity() {
    setResult(REQUEST_CODE_LOGOUT);
    this.finish();
}

前のアクティビティの onActivityResult() は、すべてのアクティビティが終了するまで、上記のコードを再度呼び出します。

于 2012-02-23T08:21:27.550 に答える
-1

1 つのオプションは、各アクティビティの onCreate でログイン状態をチェックし、ログインしていない場合は finish() を実行することです。このオプションは好きではありません。戻るボタンは引き続き使用でき、アクティビティが閉じると元に戻るからです。

やりたいことは、onStop() または onPause() メソッドで logout() と finish() を呼び出すことです。これにより、アクティビティが再びオンになったときに Android が onCreate() を呼び出さなければならなくなります。次に、あなたが言うように、 onCreate() でログイン状態を確認し、ログインしていない場合はログイン画面に転送します。

他にできることは、onResume() でログイン状態を確認し、ログインしていない場合は、finish() してログイン アクティビティを起動することです。

于 2010-06-09T17:52:09.097 に答える