web-dev-qa-db-ja.com

Androidアプリがバックグラウンドになってフォアグラウンドに戻ったことを検出する方法

しばらくしてからフォアグラウンドに戻ったときに特定の動作をするアプリを作成しようとしています。アプリがバックグラウンドに送信されたとき、またはフォアグラウンドに表示されたときを検出する方法はありますか?

346
iHorse

onPause()メソッドとonResume()メソッドは、アプリケーションがバックグラウンドに戻り、再びフォアグラウンドに戻ったときに呼び出されます。ただし、アプリケーションが初めて起動されたとき、およびアプリケーションが強制終了される前に呼び出されます。もっと読むことができますアクティビティ

バックグラウンドまたはフォアグラウンドでアプリケーションのステータスを取得する直接的な方法はありませんが、私もこの問題に直面しており、onWindowFocusChangedonStopによる解決策を見つけました。

詳細については、こちらを参照してください。Android:Androidアプリがバックグラウンドに戻って最前面に戻ったことをgetRunningTasksまたはgetRunningAppProcesses を使用して検出するソリューション

87
Swind

2018:Androidは、ライフサイクルコンポーネントを通じてこれをネイティブにサポートします。

2018年3月UPDATE:より良い解決策があります。 ProcessLifecycleOwner を参照してください。新しいアーキテクチャコンポーネント1.1.0(現時点では最新)を使用する必要がありますが、これは具体的にこれを行うために設計されています。

簡単なサンプルが提供されています この回答では ですが、それについて サンプルアプリ および ブログ投稿 を作成しました。

2014年にこの記事を書いて以来、さまざまなソリューションが生まれました。一部は機能し、一部は機能していると考えられていたでしたが、欠陥(私のものを含む!)があり、私たちはコミュニティ(Android)としてその結果に耐えることを学び、特別な場合の回避策を書きました。

1つのコードスニペットが探しているソリューションであると想定しないでください。さらに良いのは、それが何をするのか、なぜそれを行うのかを理解することです。

MemoryBossクラスは、ここで書いたように私が実際に使用したことはありませんでした。これはたまたま動作する擬似コードの一部でした。

新しいアーキテクチャコンポーネントを使用しない正当な理由がない限り(特に、特に古いAPIを対象とする場合)、それらを使用してください。完全にはほど遠いですが、ComponentCallbacks2でもありませんでした。

UPDATE/NOTES(2015年11月):人々は2つのコメントをしています。1つ目は、>=の代わりに==を使用すべきだということです。 正確な値をチェックしない。ほとんどの場合これで問題ありませんが、アプリがバックグラウンドになったときにonly実行することに注意してくださいsomethingの場合、==を使用する必要があることに注意してください- andを別のソリューション(アクティビティライフサイクルコールバックなど)と組み合わせたり、または取得できない場合がある目的の効果を実現したりします。例(そして、これは私に起こりました)-lockバックグラウンドに行くときにパスワード画面でアプリをしたい場合(あなたがそれに精通していれば1Passwordのように)、あなたは誤ってメモリが不足していて、Androidが>= TRIM_MEMORY呼び出しをトリガーするため、LOW MEMORYを突然テストしている場合、アプリをロックします。そのため、どのように/何をテストするかに注意してください。

さらに、何人かの人々はあなたが戻ってきたときに検出する方法について尋ねてきました。

私が考えることができる最も簡単な方法を以下に説明しますが、一部の人々はそれに慣れていないので、ここにいくつかの擬似コードを追加しています。 YourApplicationクラスとMemoryBossクラスがclass BaseActivity extends Activityにあると仮定します(持っていない場合は作成する必要があります)。

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

    if (mApplication.wasInBackground()) {
        // HERE YOU CALL THE CODE YOU WANT TO HAPPEN ONLY ONCE WHEN YOUR APP WAS RESUMED FROM BACKGROUND
        mApplication.setWasInBackground(false);
    }
}

Dialogsはアクティビティを一時停止できるため、onStartをお勧めします。フルスクリーンダイアログを表示するだけでアプリが「バックグラウンドになった」と思わないようにしたいと思いますが、走行距離は異なる場合があります。

そして、それだけです。 ifブロックのコードは1回のみ実行です。別のアクティビティに移動しても、新しいアクティビティ(extends BaseActivity)はwasInBackgroundfalseであると報告するため、実行されませんコード、onMemoryTrimmedが呼び出され、フラグが再びtrueに設定されるまで

お役に立てば幸いです。

更新/メモ(2015年4月):このコードですべてのコピーと貼り付けを行う前に、100%の信頼性がない可能性のあるインスタンスがいくつか見つかったことに注意してください組み合わせる必要があります他の方法と組み合わせて、最良の結果を達成します。特に、2つの既知のインスタンスがあり、onTrimMemoryコールバックの実行が保証されていません。

  1. アプリが表示されている間に電話が画面をロックした場合(nn分後にデバイスがロックした場合など)、ロックスクリーンがちょうど上にあるため、このコールバックは呼び出されません(または常にではありません)。

  2. デバイスのメモリが比較的少ない場合(およびメモリストレス下)、オペレーティングシステムはこの呼び出しを無視し、より重要なレベルに直接進むようです。

さて、アプリがいつバックグラウンドになったかを知ることがどれだけ重要かによって、アクティビティのライフサイクルなどを追跡しながら、このソリューションを拡張する必要があるかどうかが決まります。

上記を念頭に置いて、優れたQAチームを結成してください;)

END OF UPDATE

遅いかもしれませんが、Ice cream sandwich(API 14)とAboveには信頼できる方法があります。

アプリに表示されるUIがなくなると、コールバックがトリガーされます。カスタムクラスに実装できるコールバックは、 ComponentCallbacks2 (はい、2つあり)と呼ばれます。このコールバックは、APIレベル14(アイスクリームサンドイッチ)以上でのみ利用可能です

基本的に、メソッドの呼び出しを取得します。

public abstract void onTrimMemory (int level)

レベルは20以上です。

public static final int TRIM_MEMORY_UI_HIDDEN

これをテストしてきましたが、常に機能します。レベル20は、アプリが表示されなくなったためにリソースを解放したいという「提案」にすぎないためです。

公式ドキュメントを引用するには:

OnTrimMemory(int)のレベル:プロセスはユーザーインターフェイスを表示していましたが、もう表示していません。メモリをより適切に管理できるように、この時点でUIを使用した大規模な割り当てを解放する必要があります。

もちろん、これを実装して、実際に言うことを実行する必要があります(特定の時間に使用されていないメモリをパージする、未使用のコレクションをクリアするなど)可能性は無限です(他の可能性については公式ドキュメントを参照してください- より重要レベル)。

しかし、興味深いのは、OSが次のことを言っていることです。ちょっと、アプリがバックグラウンドになったのです!

そもそもあなたが知りたかったことです。

戻ったときはどのように判断しますか?

それは簡単です。「BaseActivity」があると確信しているので、onResume()を使用してcanが戻ってきたことにフラグを立てます。戻ってこないと言っているのは、上記のonTrimMemoryメソッドの呼び出しを実際に受け取ったときだけだからです。

できます。誤検知はありません。アクティビティが再開している場合は、100%の時間に戻っています。ユーザーが再び戻ると、別のonTrimMemory()呼び出しを取得します。

アクティビティ(またはさらに良いことに、カスタムクラス)をサブスクライブする必要があります。

これを常に受け​​取ることを保証する最も簡単な方法は、次のような単純なクラスを作成することです。

public class MemoryBoss implements ComponentCallbacks2 {
    @Override
    public void onConfigurationChanged(final Configuration newConfig) {
    }

    @Override
    public void onLowMemory() {
    }

    @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            // We're in the Background
        }
        // you might as well implement some memory cleanup here and be a Nice Android dev.
    }
}

これを使用するには、アプリケーションの実装(にRIGHT?があります)で次のようにします。

MemoryBoss mMemoryBoss;
@Override
public void onCreate() {
   super.onCreate();
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
      mMemoryBoss = new MemoryBoss();
      registerComponentCallbacks(mMemoryBoss);
   } 
}

Interfaceを作成する場合、elseをそのifに追加し、API 14の下で使用されるComponentCallbacks(2なし)を実装できます。そのコールバックはonLowMemory()メソッドのみを持ち、 background、ただし、メモリをトリミングするために使用する必要があります。

アプリを起動して、ホームを押します。 onTrimMemory(final int level)メソッドを呼び出す必要があります(ヒント:ログの追加)。

最後のステップは、コールバックから登録解除することです。おそらく最良の場所は、アプリのonTerminate()メソッドbutです。このメソッドは実際のデバイスでは呼び出されません。

/**
 * This method is for use in emulated process environments.  It will
 * never be called on a production Android device, where processes are
 * removed by simply killing them; no user code (including this callback)
 * is executed when doing so.
 */

したがって、本当に登録したくない状況がない限り、プロセスはOSレベルで死にかけているので、それを無視しても安全です。

ある時点で登録を解除することにした場合(たとえば、アプリをクリーンアップして終了するためのシャットダウンメカニズムを提供する場合)、次の操作を実行できます。

unregisterComponentCallbacks(mMemoryBoss);

以上です。

180

これが私がどうやってこれを解決したかです。アクティビティの遷移間に時間基準を使用すると、アプリが「バックグラウンド」になったかどうかの十分な証拠が得られる可能性が最も高いという前提で機能します。

まず、Timer、TimerTask、あるアクティビティから別のアクティビティへの移行に合理的にかかる最大ミリ秒数を表す定数を持つAndroid.app.Applicationインスタンス(MyApplicationと呼びます)を使用しました値が2s)で、アプリが「バックグラウンド」であったかどうかを示すブール値。

public class MyApplication extends Application {

    private Timer mActivityTransitionTimer;
    private TimerTask mActivityTransitionTimerTask;
    public boolean wasInBackground;
    private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000;
    ...

アプリケーションは、タイマー/タスクを開始および停止するための2つの方法も提供します。

public void startActivityTransitionTimer() {
    this.mActivityTransitionTimer = new Timer();
    this.mActivityTransitionTimerTask = new TimerTask() {
        public void run() {
            MyApplication.this.wasInBackground = true;
        }
    };

    this.mActivityTransitionTimer.schedule(mActivityTransitionTimerTask,
                                           MAX_ACTIVITY_TRANSITION_TIME_MS);
}

public void stopActivityTransitionTimer() {
    if (this.mActivityTransitionTimerTask != null) {
        this.mActivityTransitionTimerTask.cancel();
    }

    if (this.mActivityTransitionTimer != null) {
        this.mActivityTransitionTimer.cancel();
    }

    this.wasInBackground = false;
}

この解決策の最後の部分は、すべてのアクティビティのonResume()イベントとonPause()イベントから、またはできればすべての具体的なアクティビティを継承するベースActivityから、これらの各メソッドへの呼び出しを追加することです。

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

    MyApplication myApp = (MyApplication)this.getApplication();
    if (myApp.wasInBackground)
    {
        //Do specific came-here-from-background code
    }

    myApp.stopActivityTransitionTimer();
}

@Override
public void onPause()
{
    super.onPause();
    ((MyApplication)this.getApplication()).startActivityTransitionTimer();
}

そのため、ユーザーがアプリのアクティビティ間を移動しているだけの場合は、出発アクティビティのonPause()がタイマーを開始しますが、入力された新しいアクティビティが最大遷移時間に達する前にタイマーをキャンセルします。したがって、wasInBackgroundfalseになります。

一方、アクティビティがランチャー、デバイスのウェイクアップ、通話の終了などからフォアグラウンドに到達した場合は、おそらくこのイベントの前にタイマータスクが実行されたため、wasInBackground trueに設定されました。

174
d60402

編集:新しいアーキテクチャーコンポーネントは、有望なものをもたらしました: ProcessLifecycleOwner@ vokilam's answer


Google I/O talk に従った実際の解決策:

class YourApplication : Application() {

  override fun onCreate() {
    super.onCreate()
    registerActivityLifecycleCallbacks(AppLifecycleTracker())
  }

}


class AppLifecycleTracker : Application.ActivityLifecycleCallbacks  {

  private var numStarted = 0

  override fun onActivityStarted(activity: Activity?) {
    if (numStarted == 0) {
      // app went to foreground
    }
    numStarted++
  }

  override fun onActivityStopped(activity: Activity?) {
    numStarted--
    if (numStarted == 0) {
      // app went to background
    }
  }

}

はい。ここには非常に多くの奇妙な解決策があるので、この単純な解決策がうまくいくと信じるのは難しいと思います。

しかし、希望はあります。

131

ProcessLifecycleOwner もまた有望な解決策のようです。

ProcessLifecycleOwnerは、最初のアクティビティがこれらのイベントを通過するときにON_STARTON_RESUMEイベントを送出します。 ON_PAUSEON_STOP、イベントは最後のアクティビティがそれらを通過した後に遅延で送出されます。この遅延は、設定の変更によりアクティビティが破棄されて再作成された場合にProcessLifecycleOwnerがイベントを送信しないことを保証するのに十分な長さです。

実装は、同じくらい簡単にすることができます

public class AppLifecycleListener implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onMoveToForeground() {
        // app moved to foreground
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onMoveToBackground() {
       // app moved to background
    }
}

// register observer
ProcessLifecycleOwner.get().getLifecycle().addObserver(new AppLifecycleListener());

ソースコードによると、現在の遅延値は700msです。

またこの機能を使うにはdependenciesが必要です。

implementation "Android.Arch.lifecycle:extensions:1.1.1" 
annotationProcessor "Android.Arch.lifecycle:compiler:1.1.1"
87
vokilam

MartínMarconcinisの答えに基づいて(ありがとう!)私はついに信頼できる(そして非常に単純な)解決策を見つけました。

public class ApplicationLifecycleHandler implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

    private static final String TAG = ApplicationLifecycleHandler.class.getSimpleName();
    private static boolean isInBackground = false;

    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {
    }

    @Override
    public void onActivityStarted(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {

        if(isInBackground){
            Log.d(TAG, "app went to foreground");
            isInBackground = false;
        }
    }

    @Override
    public void onActivityPaused(Activity activity) {
    }

    @Override
    public void onActivityStopped(Activity activity) {
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {
    }

    @Override
    public void onLowMemory() {
    }

    @Override
    public void onTrimMemory(int i) {
        if(i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN){
            Log.d(TAG, "app went to background");
            isInBackground = true;
        }
    }
}

次に、これをあなたのApplicationクラスのあなたのonCreate()に追加してください。

public class MyApp extends Android.app.Application {

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

        ApplicationLifeCycleHandler handler = new ApplicationLifeCycleHandler();
        registerActivityLifecycleCallbacks(handler);
        registerComponentCallbacks(handler);

    }

}
64
rickul

この方法を使います。操作は簡単すぎますが、アプリで十分にテストされており、実際には「ホーム」ボタン、「戻る」ボタン、または画面ロック後にホーム画面に移動するなど、すべてのケースで驚くほどうまく機能します。試してみる。

アイデアは、フォアグラウンドでは、Androidは常に前の活動を停止する直前に新しい活動を開始します。それは保証されていませんが、それがどのように機能するかです。ところで、Flurryは同じロジックを使用しているようです(推測、私はそれをチェックしませんでしたが、それは同じイベントでフックします)。

public abstract class BaseActivity extends Activity {

    private static int sessionDepth = 0;

    @Override
    protected void onStart() {
        super.onStart();       
        sessionDepth++;
        if(sessionDepth == 1){
        //app came to foreground;
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (sessionDepth > 0)
            sessionDepth--;
        if (sessionDepth == 0) {
            // app went to background
        }
    }

}

編集:コメントに従って、コードのそれ以降のバージョンでもonStart()に移動しました。また、私は最初の投稿から欠けていたスーパーコールを追加しています。これは実用的なコードというよりは概念のためです。

59
Nick Frolov

あなたのアプリがタブバーウィジェットのように複数のアクティビティやスタックされたアクティビティで構成されている場合、onPause()とonResume()のオーバーライドは機能しません。つまり、新しいアクティビティを開始すると、新しいアクティビティが作成される前に現在のアクティビティが一時停止されます。アクティビティを終了するとき(「戻る」ボタンを使用するとき)も同様です。

私は望んでいたように動作するように思われる2つの方法を見つけました。

最初のものはGET_TASKSパーミッションを必要とし、パッケージ名を比較することによって、デバイス上でトップに走っているアクティビティがアプリケーションに属しているかどうかチェックする簡単なメソッドから成ります:

private boolean isApplicationBroughtToBackground() {
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> tasks = am.getRunningTasks(1);
    if (!tasks.isEmpty()) {
        ComponentName topActivity = tasks.get(0).topActivity;
        if (!topActivity.getPackageName().equals(context.getPackageName())) {
            return true;
        }
    }

    return false;
}

このメソッドはDroid-Fu(現在はIgnitionと呼ばれています)フレームワークで発見されました。

私が自分で実装した2番目の方法はGET_TASKSパーミッションを必要としません。これは良いことです。代わりに、実装するのがもう少し複雑です。

MainApplicationクラスには、アプリケーションで実行中のアクティビティの数を追跡する変数があります。各アクティビティのonResume()で変数を増やし、onPause()でそれを減らします。

実行中のアクティビティの数が0に達すると、以下の条件に該当する場合、アプリケーションはバックグラウンドになります。

  • 一時停止中のアクティビティは終了していません(「戻る」ボタンが使用されました)。これは、メソッドactivity.isFinishing()を使用して実行できます。
  • 新しいアクティビティー(同じパッケージ名)は開始されていません。 startActivity()メソッドをオーバーライドしてこれを示す変数を設定してからonPostResume()でリセットすることができます。これはアクティビティが作成または再開されたときに実行される最後のメソッドです。

アプリケーションがバックグラウンドで終了したことを検出できれば、アプリケーションがフォアグラウンドに戻ったときも簡単に検出できます。

55
Emil

Applicationを拡張するクラスを作成します。それから、そのオーバーライドメソッドonTrimMemory()を使うことができます。

アプリケーションがバックグラウンドに移行したかどうかを検出するために、以下を使用します。

 @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Works for Activity
            // Get called every-time when application went to background.
        } 
        else if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { // Works for FragmentActivty
        }
    }
33
Harpreet

OnUserLeaveHintの使用を検討してください。アプリがバックグラウンドになったときにのみ呼び出されます。 onPauseは他の理由で呼び出される可能性があるため、処理するコーナーケースがあります。たとえば、ユーザーが設定ページなど、アプリ内で別のアクティビティを開いた場合、メインアクティビティのonPauseメソッドはまだアプリ内にあっても呼び出されます。何が起こっているのかを追跡することはあなたが代わりにあなたが求めていることをするonUserLeaveHintコールバックを単に使うことができるときバグにつながるでしょう。

On UserLeaveHintが呼び出されると、ブール値のinBackgroundフラグをtrueに設定できます。 onResumeが呼び出されたときに、inBackgroundフラグが設定されている場合にのみ、前景に戻ったと想定してください。これは、ユーザーが設定メニューに入っていてアプリを終了したことがない場合、onResumeもメインアクティビティで呼び出されるためです。

設定画面でユーザーがホームボタンを押すと、onUserLeaveHintが設定アクティビティで呼び出され、戻ったときにonResumeが設定アクティビティで呼び出されます。あなたのメインアクティビティにこの検出コードしかない場合は、このユースケースを見逃すでしょう。コードを重複させずにすべてのアクティビティにこのコードを含めるには、Activityを拡張する抽象アクティビティクラスを作成し、その中に共通コードを入れます。それからあなたが持っているそれぞれの活動はこの抽象的な活動を拡張することができます。

例えば:

public abstract AbstractActivity extends Activity {
    private static boolean inBackground = false;

    @Override
    public void onResume() {
        if (inBackground) {
            // You just came from the background
            inBackground = false;
        }
        else {
            // You just returned from another activity within your own app
        }
    }

    @Override
    public void onUserLeaveHint() {
        inBackground = true;
    }
}

public abstract MainActivity extends AbstractActivity {
    ...
}

public abstract SettingsActivity extends AbstractActivity {
    ...
}
17
OldSchool4664

ActivityLifecycleCallbacks は興味深いかもしれませんが、文書化されていません。

ただし、 registerActivityLifecycleCallbacks ()を呼び出すと、アクティビティの作成時、破棄時などにコールバックを取得できるはずです。 getComponentName ()を呼び出すことができます。活動のために)。

13
Reno

Android.Arch.lifecycleパッケージは、ライフサイクル対応コンポーネントを構築するためのクラスとインターフェースを提供します。

アプリケーションはLifecycleObserverインターフェースを実装する必要があります。

public class MyApplication extends Application implements LifecycleObserver {

    @Override
    public void onCreate() {
        super.onCreate();
        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    private void onAppBackgrounded() {
        Log.d("MyApp", "App in background");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    private void onAppForegrounded() {
        Log.d("MyApp", "App in foreground");
    }
}

これを行うには、build.gradleファイルにこの依存関係を追加する必要があります。

dependencies {
    implementation "Android.Arch.lifecycle:extensions:1.1.1"
}

Googleが推奨するように、アクティビティのライフサイクルメソッドで実行されるコードを最小限に抑える必要があります。

一般的なパターンは、アクティビティとフラグメントのライフサイクルメソッドで依存コンポーネントのアクションを実装することです。しかし、このパターンはコードの構成が悪くなり、エラーが拡散します。ライフサイクル対応コンポーネントを使用することで、依存コンポーネントのコードをライフサイクルメソッドからコンポーネント自体に移動することができます。

あなたはここでもっと読むことができます: https://developer.Android.com/topic/libraries/architecture/lifecycle

10
matdev

アプリケーションにコールバックを追加し、次のようにルートアクティビティを確認します。

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
        @Override
        public void onActivityStopped(Activity activity) {
        }

        @Override
        public void onActivityStarted(Activity activity) {
        }

        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        }

        @Override
        public void onActivityResumed(Activity activity) {
        }

        @Override
        public void onActivityPaused(Activity activity) {
        }

        @Override
        public void onActivityDestroyed(Activity activity) {
        }

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            if (activity.isTaskRoot() && !(activity instanceof YourSplashScreenActivity)) {
                Log.e(YourApp.TAG, "Reload defaults on restoring from background.");
                loadDefaults();
            }
        }
    });
}
8

Githubでプロジェクトを作成しました app-foreground-background-listen

アプリケーション内のすべてのアクティビティに対してBaseActivityを作成します。

public class BaseActivity extends Activity {

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }

    public static boolean isAppInFg = false;
    public static boolean isScrInFg = false;
    public static boolean isChangeScrFg = false;

    @Override
    protected void onStart() {
        if (!isAppInFg) {
            isAppInFg = true;
            isChangeScrFg = false;
            onAppStart();
        }
        else {
            isChangeScrFg = true;
        }
        isScrInFg = true;

        super.onStart();
    }

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

        if (!isScrInFg || !isChangeScrFg) {
            isAppInFg = false;
            onAppPause();
        }
        isScrInFg = false;
    }

    public void onAppStart() {

        // Remove this toast
        Toast.makeText(getApplicationContext(), "App in foreground",    Toast.LENGTH_LONG).show();

        // Your code
    }

    public void onAppPause() {

        // Remove this toast
        Toast.makeText(getApplicationContext(), "App in background",  Toast.LENGTH_LONG).show();

        // Your code
    }
}

MainActivityはBaseActivityを拡張し、onAppStartはアプリケーションの起動時に呼び出され、onAppPause()はアプリケーションが任意の画面からバックグラウンドに移動したときに呼び出されます。

6
kiran boghra

これは ProcessLifecycleOwner を使えばかなり簡単です。

これらの依存関係を追加してください

implementation "Android.Arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "Android.Arch.lifecycle:compiler:$project.archLifecycleVersion"

コトリン

class ForegroundBackgroundListener : LifecycleObserver {


    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun startSomething() {
        Log.v("ProcessLog", "APP IS ON FOREGROUND")
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun stopSomething() {
        Log.v("ProcessLog", "APP IS IN BACKGROUND")
    }
}

それからあなたの基本的な活動の中で:

override fun onCreate() {
        super.onCreate()

        ProcessLifecycleOwner.get()
                .lifecycle
                .addObserver(
                        ForegroundBackgroundListener()
                                .also { appObserver = it })
    }

このトピックに関する私の記事を参照してください。 https://medium.com/@egek92/how-to-actually-detect-foreground-background-changes-in-your-Android-application-without -wanting-9719cc822c48

6

フォアグラウンドかバックグラウンドかを問わず、アプリケーションを検出するための優れた方法を見つけました。これが私の コード です。これがお役に立てば幸いです。

/**
 * Custom Application which can detect application state of whether it enter
 * background or enter foreground.
 *
 * @reference http://www.vardhan-justlikethat.blogspot.sg/2014/02/Android-solution-to-detect-when-Android.html
 */
 public abstract class StatusApplication extends Application implements ActivityLifecycleCallbacks {

public static final int STATE_UNKNOWN = 0x00;
public static final int STATE_CREATED = 0x01;
public static final int STATE_STARTED = 0x02;
public static final int STATE_RESUMED = 0x03;
public static final int STATE_PAUSED = 0x04;
public static final int STATE_STOPPED = 0x05;
public static final int STATE_DESTROYED = 0x06;

private static final int FLAG_STATE_FOREGROUND = -1;
private static final int FLAG_STATE_BACKGROUND = -2;

private int mCurrentState = STATE_UNKNOWN;
private int mStateFlag = FLAG_STATE_BACKGROUND;

@Override
public void onCreate() {
    super.onCreate();
    mCurrentState = STATE_UNKNOWN;
    registerActivityLifecycleCallbacks(this);
}

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    // mCurrentState = STATE_CREATED;
}

@Override
public void onActivityStarted(Activity activity) {
    if (mCurrentState == STATE_UNKNOWN || mCurrentState == STATE_STOPPED) {
        if (mStateFlag == FLAG_STATE_BACKGROUND) {
            applicationWillEnterForeground();
            mStateFlag = FLAG_STATE_FOREGROUND;
        }
    }
    mCurrentState = STATE_STARTED;

}

@Override
public void onActivityResumed(Activity activity) {
    mCurrentState = STATE_RESUMED;

}

@Override
public void onActivityPaused(Activity activity) {
    mCurrentState = STATE_PAUSED;

}

@Override
public void onActivityStopped(Activity activity) {
    mCurrentState = STATE_STOPPED;

}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

}

@Override
public void onActivityDestroyed(Activity activity) {
    mCurrentState = STATE_DESTROYED;
}

@Override
public void onTrimMemory(int level) {
    super.onTrimMemory(level);
    if (mCurrentState == STATE_STOPPED && level >= TRIM_MEMORY_UI_HIDDEN) {
        if (mStateFlag == FLAG_STATE_FOREGROUND) {
            applicationDidEnterBackground();
            mStateFlag = FLAG_STATE_BACKGROUND;
        }
    }else if (mCurrentState == STATE_DESTROYED && level >= TRIM_MEMORY_UI_HIDDEN) {
        if (mStateFlag == FLAG_STATE_FOREGROUND) {
            applicationDidDestroyed();
            mStateFlag = FLAG_STATE_BACKGROUND;
        }
    }
}

/**
 * The method be called when the application been destroyed. But when the
 * device screen off,this method will not invoked.
 */
protected abstract void applicationDidDestroyed();

/**
 * The method be called when the application enter background. But when the
 * device screen off,this method will not invoked.
 */
protected abstract void applicationDidEnterBackground();

/**
 * The method be called when the application enter foreground.
 */
protected abstract void applicationWillEnterForeground();

}

3
Folyd

あなたが使用することができます:

protected void onRestart()

新しい起動と再起動の間で異なります。

enter image description here

3
AYBABTU

編集2:私が以下に書いたものは実際にはうまくいきません。 GoogleはActivityManager.getRunningTasks()への呼び出しを含むアプリを拒否しました。 ドキュメント から、このAPIはデバッグと開発の目的のためだけにあることは明らかです。以下のGitHubプロジェクトを、タイマーを使用してほぼ同等の新しいスキームで更新する時間があるとすぐに、この記事を更新します。

編集1:私は ブログ記事 を書き、 簡単なGitHubリポジトリを作成しました これを本当に簡単にするために。

一般に認められていて最高と評価されている答えは、どちらも本当に最善の方法ではありません。 isApplicationBroughtToBackground()の最高評価の回答の実装は、アプリケーションのメインActivityが同じApplicationで定義されているActivityに譲るという状況を処理しませんが、それは異なるJavaパッケージを持ちます。そのような場合にうまくいくような方法を思いついた。

これをonPause()で呼び出すと、他のアプリケーションが起動したため、またはユーザーがホームボタンを押したために、アプリケーションがバックグラウンドに移行しているかどうかがわかります。

public static boolean isApplicationBroughtToBackground(final Activity activity) {
  ActivityManager activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
  List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1);

  // Check the top Activity against the list of Activities contained in the Application's package.
  if (!tasks.isEmpty()) {
    ComponentName topActivity = tasks.get(0).topActivity;
    try {
      PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
      for (ActivityInfo activityInfo : pi.activities) {
        if(topActivity.getClassName().equals(activityInfo.name)) {
          return false;
        }
      }
    } catch( PackageManager.NameNotFoundException e) {
      return false; // Never happens.
    }
  }
  return true;
}
3
Sky Kelsey

アプリケーション全体がバックグラウンド/フォアグラウンドになったときに通知する単純なライフサイクル方法はありません。

私はこれを簡単な方法で行った。アプリケーションのバックグラウンド/フォアグラウンドフェーズを検出するには、以下の手順に従ってください。

ちょっとした回避策でそれは可能です。ここでは、ActivityLifecycleCallbacksが役に立ちます。段階的に見ていきましょう

  1. まず、Android.app.Applicationを拡張し、ActivityLifecycleCallbacksインターフェースを実装するクラスを作成します。 Application.onCreate()で、コールバックを登録します。

    public class App extends Application implements 
        Application.ActivityLifecycleCallbacks {
    
        @Override
        public void onCreate() {
            super.onCreate();
            registerActivityLifecycleCallbacks(this);
        }
    }
    
  2. 以下のようにマニフェストにAppクラスを登録します<application Android:name=".App"

  3. アプリがフォアグラウンドにあるときは開始状態には少なくとも1つのActivityがあり、アプリがバックグラウンドにあるときには開始状態にはActivityはありません。

    以下のように“ App”クラスで2つの変数を宣言します。

    private int activityReferences = 0;
    private boolean isActivityChangingConfigurations = false;
    

    activityReferencesは、開始状態のアクティビティー数のカウントを保持します。 isActivityChangingConfigurationsは、現在のActivityが方向転換のように設定変更を行っているかどうかを示すフラグです。

  4. 次のコードを使用すると、アプリがフォアグラウンドになったかどうかを検出できます。

    @Override
    public void onActivityStarted(Activity activity) {
        if (++activityReferences == 1 && !isActivityChangingConfigurations) {
            // App enters foreground
        }
    }
    
  5. これは、アプリがバックグラウンドになったかどうかを検出する方法です。

    @Override
    public void onActivityStopped(Activity activity) {
        isActivityChangingConfigurations = activity.isChangingConfigurations();
        if (--activityReferences == 0 && !isActivityChangingConfigurations) {
            // App enters background
        }
    }
    

仕組み:

これは、ライフサイクルメソッドが順番に呼び出される方法を使って行われるちょっとしたトリックです。シナリオを見てみましょう。

ユーザーがアプリを起動し、Launcher Activity Aが起動されたとします。ライフサイクルコールは、

A.onCreate()

A.onStart()(++ activityReferences == 1)(アプリはフォアグラウンドに入ります)

A.onResume()

ここで活動Aが活動Bを開始します。

A.onPause()

B.onCreate()

B.onStart()(++ activityReferences == 2)

B.onResume()

A.onStop()(--activityReferences == 1)

その後、ユーザーはアクティビティBから戻ります。

B.onPause()

A.onStart()(++ activityReferences == 2)

A.onResume()

B.onStop()(--activityReferences == 1)

B.onDestroy()

それからユーザーはホームボタンを押します、

A.onPause()

A.onStop()(--activityReferences == 0)(アプリはバックグラウンドに入ります)

この場合、ユーザーが[戻る]ボタンではなく[アクティビティB]から[ホーム]ボタンを押しても、それは同じになり、activityReferencesは0になります。そのため、アプリがバックグラウンドに入ったことを検出できます。

それで、isActivityChangingConfigurationsの役割は何ですか?上記のシナリオで、アクティビティBが方向を変更したとします。コールバックシーケンスは次のようになります。

B.onPause()

B.onStop()(--activityReferences == 0)(アプリはバックグラウンドに入ります??)

B.onDestroy()

B.onCreate()

B.onStart()(++ activityReferences == 1)(アプリケーションはフォアグラウンドに入りますか?)

B.onResume()

そのため、アクティビティが設定の変更を行っているときにシナリオを回避するために、isActivityChangingConfigurationsの追加チェックを行う必要があります。

2
Komal Nikhare

ここで正解

以下のようにMyAppという名前のクラスを作成します。

public class MyApp implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

    private Context context;
    public void setContext(Context context)
    {
        this.context = context;
    }

    private boolean isInBackground = false;

    @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {


            isInBackground = true;
            Log.d("status = ","we are out");
        }
    }


    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    @Override
    public void onActivityResumed(Activity activity) {

        if(isInBackground){

            isInBackground = false;
            Log.d("status = ","we are in");
        }

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {

    }

    @Override
    public void onLowMemory() {

    }
}

それから、あなたが望むすべての場所(アプリで起動されたより良い最初のアクティビティ)に、以下のコードを追加してください:

MyApp myApp = new MyApp();
registerComponentCallbacks(myApp);
getApplication().registerActivityLifecycleCallbacks(myApp);

完了しました。アプリがバックグラウンドにあるとき、私たちはログstatus : we are outを得ます、そして私たちがアプリに入るとき、私たちはログstatus : we are outを得ます

2
erfan

これは@ d60402の答えの修正版です: https://stackoverflow.com/a/15573121/4747587

そこに言及されているすべてをしなさい。しかし、Base Activityを持ち、それをすべてのアクティビティの親にしてonResume()onPauseをオーバーライドする代わりに、以下の手順を実行します。

アプリケーションクラスに次の行を追加します。

registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacksコールバック);

このcallbackにはすべてのアクティビティライフサイクルメソッドがあり、onActivityResumed()onActivityPaused()をオーバーライドすることができます。

この要旨を見てください。 https://Gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b

1
Henry

ProcessLifecycleOwner を使用して、ライフサイクルオブザーバをアタッチできます。

  public class ForegroundLifecycleObserver implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void onAppCreated() {
        Timber.d("onAppCreated() called");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onAppStarted() {
        Timber.d("onAppStarted() called");
    }

    @OnLifecycleEvent(Event.ON_RESUME)
    public void onAppResumed() {
        Timber.d("onAppResumed() called");
    }

    @OnLifecycleEvent(Event.ON_PAUSE)
    public void onAppPaused() {
        Timber.d("onAppPaused() called");
    }

    @OnLifecycleEvent(Event.ON_STOP)
    public void onAppStopped() {
        Timber.d("onAppStopped() called");
    }
}

次に、アプリケーションクラスのonCreate()でこれを呼び出します。

ProcessLifecycleOwner.get().getLifecycle().addObserver(new ForegroundLifecycleObserver());

これにより、アプリケーションがバックグラウンドになったときに発生するON_PAUSEおよびON_STOPのイベントをキャプチャできます。

1

私はこれをGoogle Analytics EasyTrackerで使っていましたが、うまくいきました。単純な整数を使ってあなたが求めることをするように拡張することができます。

public class MainApplication extends Application {

    int isAppBackgrounded = 0;

    @Override
    public void onCreate() {
        super.onCreate();
        appBackgroundedDetector();
    }

    private void appBackgroundedDetector() {
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityStarted(Activity activity) {
                EasyTracker.getInstance(MainApplication.this).activityStart(activity);
            }

            @Override
            public void onActivityResumed(Activity activity) {
                isAppBackgrounded++;
                if (isAppBackgrounded > 0) {
                    // Do something here
                }
            }

            @Override
            public void onActivityPaused(Activity activity) {
                isAppBackgrounded--;
            }

            @Override
            public void onActivityStopped(Activity activity) {
                EasyTracker.getInstance(MainApplication.this).activityStop(activity);
            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }
}
1
Bill Mote

私はそれが少し遅れているのを知っています、しかし、私はそれが以下のようにした間、私はこれらのすべての答えがいくつかの問題を抱えていると思います、そしてそれは完璧に働きます。

次のようにアクティビティライフサイクルコールバックを作成します。

 class ActivityLifeCycle implements ActivityLifecycleCallbacks{

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    Activity lastActivity;
    @Override
    public void onActivityResumed(Activity activity) {
        //if (null == lastActivity || (activity != null && activity == lastActivity)) //use this condition instead if you want to be informed also when  app has been killed or started for the first time
        if (activity != null && activity == lastActivity) 
        {
            Toast.makeText(MyApp.this, "NOW!", Toast.LENGTH_LONG).show();
        }

        lastActivity = activity;
    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }
}

以下のようにアプリケーションクラスに登録してください。

public class MyApp extends Application {

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifeCycle());
}
1
Amir Ziarati

(これを書いている時点では)AndroidはapplicationDidEnterBackground()applicationWillEnterForeground()コールバックと同等のiOSを持っていないので、これはAndroidで最も複雑な質問の1つのようです。 AppStateライブラリ@ jenzz でまとめて使用しました。

[AppState]はRxJavaをベースにしたシンプルで反応性の高いAndroidライブラリで、アプリの状態変化を監視します。アプリがバックグラウンドになり、フォアグラウンドに戻るたびに、購読者に通知します。

特に私のアプリは複数のアクティビティを持っていたので、アクティビティのonStart()onStop()をチェックするだけではうまくいきませんでした。

最初にこれらの依存関係をgradleに追加しました:

dependencies {
    compile 'com.jenzz.appstate:appstate:3.0.1'
    compile 'com.jenzz.appstate:adapter-rxjava2:3.0.1'
}

それからそれはあなたのコードの適切な場所にこれらの行を追加することの簡単な問題でした:

//Note that this uses RxJava 2.x adapter. Check the referenced github site for other ways of using observable
Observable<AppState> appState = RxAppStateMonitor.monitor(myApplication);
//where myApplication is a subclass of Android.app.Application
appState.subscribe(new Consumer<AppState>() {
    @Override
    public void accept(@io.reactivex.annotations.NonNull AppState appState) throws Exception {
        switch (appState) {
            case FOREGROUND:
                Log.i("info","App entered foreground");
                break;
            case BACKGROUND:
                Log.i("info","App entered background");
                break;
        }
    }
});

あなたがどのようにオブザーバブルを購読するかによって、あなたはメモリリークを避けるためにそれから購読を中止しなければならないかもしれません。 githubページ にもまた詳しい情報があります。

1
Deniz

タイムスタンプをチェックせずに回転も処理するアプローチが見つからなかったので、私たちのアプリでもこれを実行する方法を共有したと思いました。この答えへの唯一の追加 https://stackoverflow.com/a/42679191/5119746 は、オリエンテーションも考慮に入れることです。

class MyApplication : Application(), Application.ActivityLifecycleCallbacks {

   // Members

   private var mAppIsInBackground = false
   private var mCurrentOrientation: Int? = null
   private var mOrientationWasChanged = false
   private var mResumed = 0
   private var mPaused = 0

その後、コールバックのために私達は最初に履歴書を持っています:

   // ActivityLifecycleCallbacks

   override fun onActivityResumed(activity: Activity?) {

      mResumed++

      if (mAppIsInBackground) {

         // !!! App came from background !!! Insert code

         mAppIsInBackground = false
      }
      mOrientationWasChanged = false
    }

そしてonActivityStopped:

   override fun onActivityStopped(activity: Activity?) {

       if (mResumed == mPaused && !mOrientationWasChanged) {

       // !!! App moved to background !!! Insert code

        mAppIsInBackground = true
    }

そして、これが追加されます:方向の変化をチェックする:

   override fun onConfigurationChanged(newConfig: Configuration) {

       if (newConfig.orientation != mCurrentOrientation) {
           mCurrentOrientation = newConfig.orientation
           mOrientationWasChanged = true
       }
       super.onConfigurationChanged(newConfig)
   }

それでおしまい。これが誰かに役立つことを願っています:)

1
Julian Horst

私の解決策は@ d60402の答えに触発されたもので、タイムウィンドウにも依存していますが、Timerは使用していません。

public abstract class BaseActivity extends ActionBarActivity {

  protected boolean wasInBackground = false;

  @Override
  protected void onStart() {
    super.onStart();
    wasInBackground = getApp().isInBackground;
    getApp().isInBackground = false;
    getApp().lastForegroundTransition = System.currentTimeMillis();
  }

  @Override
  protected void onStop() {
    super.onStop();
    if( 1500 < System.currentTimeMillis() - getApp().lastForegroundTransition )
      getApp().isInBackground = true;
  }

  protected SingletonApplication getApp(){
    return (SingletonApplication)getApplication();
  }
}

ここで、SingletonApplicationApplicationクラスの拡張です。

public class SingletonApplication extends Application {
  public boolean isInBackground = false;
  public long lastForegroundTransition = 0;
}
1
injecteer

以下のようなActivityLifecycleCallbacksComponentCallbacks2の助けを借りてこれを簡単に達成することができます。

上記のインターフェースを実装するクラスAppLifeCycleHandlerを作成します。

package com.sample.app;

import Android.app.Activity;
import Android.app.Application;
import Android.content.ComponentCallbacks2;
import Android.content.res.Configuration;
import Android.os.Bundle;

/**
 * Created by Naveen on 17/04/18
 */
public class AppLifeCycleHandler
    implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

  AppLifeCycleCallback appLifeCycleCallback;

  boolean appInForeground;

  public AppLifeCycleHandler(AppLifeCycleCallback appLifeCycleCallback) {
    this.appLifeCycleCallback = appLifeCycleCallback;
  }

  @Override
  public void onActivityResumed(Activity activity) {
    if (!appInForeground) {
      appInForeground = true;
      appLifeCycleCallback.onAppForeground();
    }
  }

  @Override
  public void onTrimMemory(int i) {
    if (i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
      appInForeground = false;
      appLifeCycleCallback.onAppBackground();
    }
  }

  @Override
  public void onActivityCreated(Activity activity, Bundle bundle) {

  }

  @Override
  public void onActivityStarted(Activity activity) {

  }

  @Override
  public void onActivityPaused(Activity activity) {

  }

  @Override
  public void onActivityStopped(Activity activity) {

  }

  @Override
  public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

  }

  @Override
  public void onActivityDestroyed(Activity activity) {

  }

  @Override
  public void onConfigurationChanged(Configuration configuration) {

  }

  @Override
  public void onLowMemory() {

  }

  interface AppLifeCycleCallback {

    void onAppBackground();

    void onAppForeground();
  }
}

Applicationを拡張するクラスでは、アプリがフォアグラウンドとバックグラウンドを切り替えたときにコールバックを取得するためにAppLifeCycleCallbackを実装します。以下のようなもの。

public class BaseApplication extends Application implements AppLifeCycleHandler.AppLifeCycleCallback{

    @Override
    public void onCreate() {
        super.onCreate();
        AppLifeCycleHandler appLifeCycleHandler = new AppLifeCycleHandler(this);
        registerActivityLifecycleCallbacks(appLifeCycleHandler);
        registerComponentCallbacks(appLifeCycleHandler);
    }

    @Override
    public void onAppBackground() {
        Log.d("LifecycleEvent", "onAppBackground");
    }

    @Override
    public void onAppForeground() {
        Log.d("LifecycleEvent", "onAppForeground");
    }
}

お役に立てれば。

EDIT代わりに、ライフサイクル対応アーキテクチャコンポーネントを使用することもできます。

1
Naveen T P

これは私の解決策です https://github.com/doridori/AndroidUtils/blob/master/App/ src/main/Java/com/doridori/lib/app/ActivityCounter.Java

基本的には、現在フォアグラウンドにアクティビティがなく、アプリがローテーションの場合など、すべてのアクティビティのライフサイクルメソッドをタイマーでカウントしてキャッチします。

0
Dori

私は、onResume、onPause、およびonStopアクティビティコールバックの使用を活用するBaseActivityを実装することによって、バックグラウンドからフォアグラウンドに戻るアプリケーションナビゲーションを監視することができました。これが私の実装です。

override fun onResume() {
    super.onResume()
    if (AppActivityState.state == AppState.ON_LAUNCHED) {
        // We are in the first launch.
        onLaunched()
    } else {
        if (AppActivityState.state == AppState.ON_BACKGROUND) {
            // We came from background to foreground.
            AppActivityState.state = AppState.ON_FOREGROUND
            onForeground()
        } else {
            // We are just navigating through pages.
            AppActivityState.state = AppState.RESUMED
        }
    }
}

override fun onPause() {
    super.onPause()
    // If state is followed by onStop then it means we will going to background.
    AppActivityState.state = AppState.PAUSED
}

override fun onStop() {
    super.onStop()

    // App will go to background base on the 'pause' cue.
    if (AppActivityState.state == AppState.PAUSED) {
        AppActivityState.state = AppState.ON_BACKGROUND
        onBackground()
    }
}

BaseActivityを作成したら、このアクティビティをアプリ上の任意のアクティビティに拡張するだけです。

これらのタイプの実装では、以下を正確に検出できます。 - onBackground>アプリがバックグラウンドに移動します - onForeground>アプリがフォアグラウンドに戻ります - onLaunch>アプリが開いたばかり

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

0
Rhusfer

これが私の解決策です。このActivityLifecycleCallbacksをメインのApplicationクラスに登録するだけです。コメントでは、私はユーザープロファイルのActivity Edgeのケースについて述べています。そのアクティビティは、単純に透明な端を持つアクティビティです。

/**
 * This class used Activity lifecycle callbacks to determine when the application goes to the
 * background as well as when it is brought to the foreground.
 */
public class Foreground implements Application.ActivityLifecycleCallbacks
{
    /**
     * How long to wait before checking onStart()/onStop() count to determine if the app has been
     * backgrounded.
     */
    public static final long BACKGROUND_CHECK_DELAY_MS = 500;

    private static Foreground sInstance;

    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
    private boolean mIsForeground = false;
    private int mCount;

    public static void init(final Application application)
    {
        if (sInstance == null)
        {
            sInstance = new Foreground();
            application.registerActivityLifecycleCallbacks(sInstance);
        }
    }

    public static Foreground getInstance()
    {
        return sInstance;
    }

    public boolean isForeground()
    {
        return mIsForeground;
    }

    public boolean isBackground()
    {
        return !mIsForeground;
    }

    @Override
    public void onActivityStarted(final Activity activity)
    {
        mCount++;

        // Remove posted Runnables so any Meteor disconnect is cancelled if the user comes back to
        // the app before it runs.
        mMainThreadHandler.removeCallbacksAndMessages(null);

        if (!mIsForeground)
        {
            mIsForeground = true;
        }
    }

    @Override
    public void onActivityStopped(final Activity activity)
    {
        mCount--;

        // A transparent Activity like community user profile won't stop the Activity that launched
        // it. If you launch another Activity from the user profile or hit the Android home button,
        // there are two onStops(). One for the user profile and one for its parent. Remove any
        // posted Runnables so we don't get two session ended events.
        mMainThreadHandler.removeCallbacksAndMessages(null);
        mMainThreadHandler.postDelayed(new Runnable()
        {
            @Override
            public void run()
            {
                if (mCount == 0)
                {
                    mIsForeground = false;
                }
            }
        }, BACKGROUND_CHECK_DELAY_MS);
    }

    @Override
    public void onActivityCreated(final Activity activity, final Bundle savedInstanceState)
    {

    }

    @Override
    public void onActivityResumed(final Activity activity)
    {

    }

    @Override
    public void onActivityPaused(final Activity activity)
    {

    }

    @Override
    public void onActivitySaveInstanceState(final Activity activity, final Bundle outState)
    {

    }

    @Override
    public void onActivityDestroyed(final Activity activity)
    {

    }
}
0
Stephen

LiveDataを使って この解法 を展開することができます。

class AppForegroundStateLiveData : LiveData<AppForegroundStateLiveData.State>() {

    private var lifecycleListener: LifecycleObserver? = null

    override fun onActive() {
        super.onActive()
        lifecycleListener = AppLifecycleListener().also {
            ProcessLifecycleOwner.get().lifecycle.addObserver(it)
        }
    }

    override fun onInactive() {
        super.onInactive()
        lifecycleListener?.let {
            this.lifecycleListener = null
            ProcessLifecycleOwner.get().lifecycle.removeObserver(it)
        }
    }

    internal inner class AppLifecycleListener : LifecycleObserver {

        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        fun onMoveToForeground() {
            value = State.FOREGROUND
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        fun onMoveToBackground() {
            value = State.BACKGROUND
        }
    }

    enum class State {
        FOREGROUND, BACKGROUND
    }
}

これで、このLiveDataを購読して必要なイベントをキャッチすることができます。例えば:

appForegroundStateLiveData.observeForever { state ->
    when(state) {
        AppForegroundStateLiveData.State.FOREGROUND -> { /* app move to foreground */ }
        AppForegroundStateLiveData.State.BACKGROUND -> { /* app move to background */ }
    }
}
0
Alex Kisel

私のアプリはバックグラウンドから戻った後に「再起動」する必要があります - クライアントの要請によると、一連のアクティビティを表示します。背景と前景の遷移を管理する方法(iOSとAndroidの間では非常に異なる扱いをしました)を徹底的に調査した後、私はこの質問を乗り越えました。特に最も投票された回答と正しいものとしてフラグが立てられたものから、ここで非常に役に立つ助けを見つけました。しかし、あなたがUXについて考えるとき、アプリがフォアグラウンドに入るたびに単にルートアクティビティを再インスタンス化することはあまりにも面倒に見えます。私にとって効果的な解決策、そして私が最も適切だと思う解決策は、YoutubeとTwitterのアプリの機能に基づいて、次のように@GirishNairと@ d60402からの回答を組み合わせることでした。

@Override
public void onTrimMemory(int level) {
    if (stateOfLifeCycle.equals("Stop")) {
        startActivityTransitionTimer();
    }

    super.onTrimMemory(level);
}

私のタイマー制限は30秒に設定されています - 私はこれを少し増やすことを考えています。

private final long MAX_ACTIVITY_TRANSITION_TIME = 30000;

そして、アプリがフォアグラウンドになったとき、再起動されたとき、またはアプリが破棄されたときに、タイマーをキャンセルするためのメソッドを呼び出します。

アプリ拡張時:

@Override
public void onActivityCreated(Activity activity, Bundle arg1) {
    stopActivityTransitionTimer();
    stateOfLifeCycle = "Create";
}

@Override
public void onActivityDestroyed(Activity activity) {
    stopActivityTransitionTimer();
    stateOfLifeCycle = "Destroy";
}

活動について(できれば基本活動で、他の人が受け継いでいる):

@Override
protected void onStart() {
    super.onStart();
    if (App.wasInBackground) {
        stopActivityTransitionTimer();
    }
}

私の場合、アプリが最大時間の経過後にフォアグラウンドになると、新しいタスクが作成されるため、アプリケーション拡張クラスのonActivityCreated()またはonActivityDestroyed()でstopActivityTransitionTimer()が呼び出されます。アクティビティ内のメソッドを呼び出す必要はありません。それが役に立てば幸い。

0
Pablo

この解決策はどうですか

public class BaseActivity extends Activity
{

    static String currentAct = "";

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

        if (currentAct.equals(""))
            Toast.makeText(this, "Start", Toast.LENGTH_LONG).show();

        currentAct = getLocalClassName();
    }

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

        if (currentAct.equals(getLocalClassName()))
        {
            currentAct = "";
            Toast.makeText(this, "Stop", Toast.LENGTH_LONG).show();
        }
    }
}

すべてのアクティビティはBaseActivityを拡張する必要があります。

あるアクティビティが別のアクティビティ(A-> B)を呼び出すと、2番目のアクティビティ(B)のonStart()が最初の(A)のonStop()の前に呼び出されるため、currentActはgetLocalClassName()と等しくなりません( https://developer.Android.com/guide/components/activities.html#CoordinatingActivities )。

ユーザーがホームボタンを押すか、アプリケーションを切り替えると、onStop()が呼び出され、currentActはgetLocalClassName()と等しくなります。

0
Ismael

以下のコードを使用することによって、私は私のアプリをフォアグラウンドまたはバックグラウンド状態にすることができます。

それが動いていることの詳細については、ここをクリックしてください 強いテキスト

import Android.content.ComponentCallbacks2;
import Android.content.Context;
import Android.support.v7.app.AppCompatActivity;
import Android.os.Bundle;
import Android.widget.Toast;

public class MainActivity extends AppCompatActivity {

private Context context;
private Toast toast;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    context = this;
}

private void showToast(String message) {
    //If toast is already showing cancel it
    if (toast != null) {
        toast.cancel();
    }

    toast = Toast.makeText(context, message, Toast.LENGTH_SHORT);
    toast.show();
}

@Override
protected void onStart() {
    super.onStart();
    showToast("App In Foreground");
}

@Override
public void onTrimMemory(int level) {
    super.onTrimMemory(level);
    if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
        showToast("App In Background");
    }
  }
}
0
Jitendra Singh

私がしたことは、すべてのアプリ内アクティビティがstartActivityForResultで起動され、onActivityResultがonResumeの前に呼び出されたかどうかを確認することです。そうでなかったら、それは私達が私達のアプリの外のどこかから戻ってきたことを意味します。

boolean onActivityResultCalledBeforeOnResume;

@Override
public void startActivity(Intent intent) {
    startActivityForResult(intent, 0);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);
    onActivityResultCalledBeforeOnResume = true;
}

@Override
protected void onResume() {
    super.onResume();
    if (!onActivityResultCalledBeforeOnResume) {
        // here, app was brought to foreground
    }
    onActivityResultCalledBeforeOnResume = false;
}
0
arturh

これらの答えは正しくないようです。これらのメソッドは、他のアクティビティが開始および終了したときにも呼び出されます。できることはグローバルフラグを保持することです(そう、グローバルは悪いです:)、あなたが新しい活動を始めるたびにこれをtrueに設定してください。各アクティビティのonCreateでfalseに設定してください。次に、onPauseでこのフラグをチェックします。それが偽であるならば、あなたのアプリはバックグラウンドになっているか、それは殺されつつあります。

0
Joris Weimar