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


382

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


2
明らかではないように見えるため、質問にユースケースを追加することになる可能性があります。そのため、与えられた回答では取り上げられていません。アプリは別のアプリ(ギャラリーなど)を起動する場合がありますが、それらは同じスタックに常駐し、アプリの画面の1つとして表示された後、ホームボタンを押します。アプリのライフサイクル(またはメモリ管理)に依存するメソッドは、これを検出できません。バックグラウンド状態は、ホームを押すときではなく、外部アクティビティが表示されたときにトリガーされます。
Dennis K

これはあなたが探している答えです:stackoverflow.com/a/42679191/2352699
FredPorciúncula17年

1
Googleのソリューションを参照してください。stackoverflow.com/questions/3667022/...
user1269737

回答:


98

onPause()そしてonResume()、アプリケーションが再びバックグラウンドにし、フォアグラウンドに持ち込まれたときにメソッドが呼び出されます。ただし、アプリケーションが初めて起動されたときや、アプリケーションが強制終了される前にも呼び出されます。詳しくは、アクティビティをご覧ください。

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

詳細については、こちらを確認してくださいAndroid:Androidアプリがバックグラウンドに移行し、getRunningTasksまたはgetRunningAppProcessesを使用せずにフォアグラウンドに戻ったときに検出するソリューション


174
ただし、これらのメソッドは同じアプリ内のアクティビティ間で遷移するときにも呼び出されるため、他の人が指摘したように、このアプローチは誤検知を引き起こします。
ジョンレーマン

9
それよりも悪いです。試してみましたが、電話がロックされているときにonResumeが呼び出されることがあります。ドキュメントでonResumeの定義を見つけた場合、次のことがわかります。onResumeは、アクティビティがユーザーに表示される最良のインジケータではないことに注意してください。キーガードなどのシステムウィンドウが前面にある場合があります。onWindowFocusChanged(boolean)を使用して、ユーザーのアクティビティがユーザーに表示されていることを確認します(たとえば、ゲームを再開するため)。developer.android.com/reference/android/app/...
J-ROU

2
リンクに投稿されたソリューションは、onResume / onPauseを使用せず、onBackPressed、onStop、onStart、onWindowsFocusChangedの組み合わせを使用します。それは私にとってはうまくいきました、そして私はかなり複雑なUI階層を持っています(引き出し、動的なビューページャーなどで)
Martin Marconcini 2013

18
onPauseとonResumeはアクティビティ固有です。アプリケーションではありません。アプリをバックグラウンドにしてから再開すると、バックグラウンドに移行する前にあった特定のアクティビティが再開されます。つまり、アプリケーションのすべてのアクティビティで、バックグラウンドからの再開時に実行したいことを実装する必要があります。元の質問は、アクティビティではなくアプリケーションの「onResume」のようなものを探していたと思います。
SysHex 2013

4
このような一般的なニーズに対して適切なAPIが提供されていないことは信じられません。最初はonUserLeaveHint()がそれをカットすると思っていましたが、ユーザーがアプリケーションを離れているかどうかは
わかり

197

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

2018年3月の更新:より良いソリューションがあります。ProcessLifecycleOwnerを参照してください。新しいアーキテクチャコンポーネント1.1.0(現時点では最新)を使用する必要がありますが、これはそのために特別に設計されています。

この回答には簡単なサンプルが用意されていますが、サンプルアプリとそれに関するブログ投稿を書きました。

これを2014年に書いて以来、さまざまな解決策が生まれました。一部は機能し、一部は機能していると考えられていましたが、欠陥(私のものを含む)があり、私たちはコミュニティ(Android)として結果に耐えることを学び、特別なケースの回避策を書きました。

単一のコードスニペットがあなたが探しているソリューションであると思い込まないでください。さらに良いことに、それが何をするのか、なぜそれをするのかを理解するようにしてください。

MemoryBossここに書かれたクラスは実際に私が使用していなかった、それが仕事に起こった擬似コードのちょうど作品でした。

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

更新/注(2015年11月):人々は2つのコメントを書いています。1つ目は、正確な値を確認するべきではないとドキュメントに記載されているため、>=代わりに使用する必要があるということです。これは、ほとんどの場合の罰金ですが、あなたがあれば心に留めていることだけやって気に何かアプリがバックグラウンドに行って、あなたが使う==する必要がありますし、あなたも(活動ライフサイクルコールバックのような)他のソリューションとそれを組み合わせますか、ご希望の効果が得られない場合があります。例(これは私に起こりました)は、ロックしたい場合==バックグラウンドに移行したときにパスワード画面が表示されるアプリ(慣れている場合は1Passwordなど)で、メモリが不足して突然テストしていると>= TRIM_MEMORY、誤ってアプリがロックされる可能性があります。AndroidがLOW MEMORY通話をトリガーするためです。あなたのものより高い。したがって、どのように/何をテストするかに注意してください。

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

私が考えることができる最も簡単な方法は以下に説明されていますが、一部の人々はそれに慣れていないため、ここにいくつかの疑似コードを追加しています。あなたがYourApplicationMemoryBossクラスを持っていると仮定します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);
    }
}

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

そして、それだけです。ifブロックのコードは1回だけ実行されます。別のアクティビティに移動した場合でも、新しい(それもextends BaseActivity)レポートwasInBackgroundfalseが呼び出されてフラグが再びtrueに設定されるまでonMemoryTrimmedコードを実行しないように報告します

お役に立てば幸いです。

更新/メモ(2015年4月):このコードをすべてコピーアンドペーストする前に、100%信頼性が低く、他の方法と組み合わせて最良の結果を得る必要がある場合があることに気づきました。注目すべきは、ある二つの既知のインスタンスonTrimMemoryのコールバックが実行されることが保証されていませんが。

  1. アプリが表示されているときに携帯電話が画面をロックする場合(たとえば、nn分後にデバイスがロックされるなど)、ロックスクリーンは真上にあるため、このコールバックは呼び出されません(または常に呼び出されるわけではありません)が、アプリは覆われていますが、まだ「実行中」です。

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

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

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

更新の終わり

遅いかもしれませんが、Ice Cream Sandwich(API 14)以降には信頼できる方法があります

アプリに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が次のように伝えていることです。HEY、アプリがバックグラウンドに移行しました。

そもそも、まさにあなたが知りたかったことです。

いつ戻ったかをどのように判断しますか?

簡単だまあ、私はあなたがそうあなたが「BaseActivity」を持っていると確信している可能フラグにという事実は、あなたしているバックをあなたのonResume()を使用します。戻ってこないと言うのは、実際に上記の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してifComponentCallbacksAPI 14以下で使用される(2なしで)実装できます。そのコールバックにはonLowMemory()メソッドしかなく、バックグラウンド移動したときに呼び出されませんが、メモリをトリムするために使用する必要があります。

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

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

/**
 * 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);

以上です。


サービスからこれをチェックすると、ホームボタンが押されたときにのみ起動するようです。戻るボタンを押しても、KitKatではこれは発生しません。
OpenGL ESを14

サービスにはUIがないため、それに関連している可能性があります。サービスではなく、基本アクティビティでチェックを行います。UIがいつ非表示になるかを知りたい(そしておそらくサービスに通知して、それがフォアグラウンドになる)
Martin Marconcini 2014

1
スマートフォンの電源を切ると機能しません。トリガーされません。
Juangcg 2014

2
Martinのおかげで、ComponentCallbacks2.onTrimMemory()を(ActivityLifecycleCallbacksと組み合わせて)使用することが、これまでに見つけた唯一の信頼できるソリューションです。興味のある方は、私の回答をご覧ください。
rickul

3
私は1年前からこの方法を使用しており、常に信頼できる方法です。他の人もそれを使うのを知っているのは良いことです。level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDENアップデート2の問題を回避するために使用するだけです。ポイント2については、アプリは実際にはバックグラウンドに移動しなかったので、私には関係ありません。
sorianiv 2015年

175

これを解決する方法を以下に示します。アクティビティの遷移間で時間参照を使用すると、アプリが「バックグラウンド」であるかどうかの十分な証拠が得られる可能性が高いという前提で機能します。

最初に、1つのアクティビティから別のアクティビティへの遷移が合理的に取ることができる最大ミリ秒数を表す定数である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()イベントから、またはできればすべての具体的なアクティビティが継承するベースアクティビティから、これらの各メソッドへの呼び出しを追加することです。

@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になります

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


4
こんにちはd60402、あなたの答えは本当に役に立ちます。この返信に感謝します...小さな通知.. MyApplicationはandroid:name = "MyApplication"のようなマニフェストファイルアプリケーションタグで言及する必要があります。私のような人
praveenb 2013

2
偉大なプログラマーのマーク、私が遭遇した最も複雑な問題の1つに対する簡単な解決策。
アーシシュバトナガール2014

2
素晴らしいソリューション!ありがとう。誰かが「ClassCastException」エラーを受け取った場合は、Manifest.xml <application android:name = "your.package.MyApplication"内のアプリケーションタグにそれを追加できなかった可能性があります
Wahib Ul Haq

27
これは素晴らしくてシンプルな実装です。ただし、これはonPause / onResumeではなく、onStart / onStopに実装する必要があると思います。アクティビティを部分的にカバーするダイアログを開始しても、onPauseが呼び出されます。ダイアログを閉じると、実際にはonResumeが呼び出され、アプリがフォアグラウンドになったように
見えます

7
このソリューションのバリエーションを使用したいと思っています。上記の対話についてのポイントは私にとって問題なので、@ Shubhayuの提案(onStart / onStop)を試しました。ただし、A-> Bの場合、アクティビティBのonStart()がアクティビティAのonStop()の前に呼び出されるため、これは役に立ちません。
Trevor

150

編集:新しいアーキテクチャコンポーネントは有望なものをもたらしました:ProcessLifecycleOwner@ vokilamの回答を参照してください


Google I / Oトークによる実際のソリューション:

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

}

はい。ここには非常に多くの奇妙な解決策があるので、この単純な解決策が機能するとは信じがたいです。

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


3
これは完璧に動作します!私はすでに非常に多くの欠陥があった非常に多くの奇妙な解決策を試しました...とても感謝しています!私はこれをしばらく探していました。
エガキンベーコンウォーカー2017年

7
これは複数のアクティビティで機能しますが、1つの場合-onrotateは、すべてのアクティビティがなくなったかバックグラウンドにあることを示します
デッドフィッシュ

2
@Shyriあなたは正しいですが、それはこのソリューションの一部なので、心配する必要があります。firebaseがこれに依存している場合、私の平凡なアプリもできると思います:)すばらしい答えです。
ElliotM 2017

3
@deadfish回答の上部にあるI / Oへのリンクを確認してください。アクティビティの停止と開始の間の時間ギャップをチェックして、本当にバックグラウンドに行ったかどうかを判断できます。これは実際には素晴らしいソリューションです。
Alex Berdnikov 2017

2
Javaソリューションはありますか?これはコトリンです。
Giacomo Bartoli

116

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

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

実装は次のように単純にすることができます

class AppLifecycleListener : LifecycleObserver {

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

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

// register observer
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleListener())

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

また、この機能を使用するには、dependencies次のものが必要です。

implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"

10
ライフサイクルの依存関係を追加する必要がimplementation "android.arch.lifecycle:extensions:1.0.0"ありannotationProcessor "android.arch.lifecycle:compiler:1.0.0"、Googleのリポジトリ(つまりgoogle())から追加する必要があることに注意してください
Codesalot卿

1
これは私にとってはうまくいきました、ありがとう。Androidの依存関係のコンパイルクラスパスとランタイムクラスパスのバージョンが異なるというエラーが発生したため、実装の代わりにapi 'android.arch.lifecycle:extensions:1.1.0'を使用する必要がありました。
FSUWX2011 2018年

アクティビティ参照がなくてもモジュールで機能するため、これは優れたソリューションです。
最大

アプリがクラッシュした場合、これは機能しません。この溶液に、イベントをクラッシュしたアプリを取得する任意の解決策がある
tejraj

素晴らしいソリューション。私の日を救った。
サニー

69

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

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

public class MyApp extends android.app.Application {

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

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

    }

}

これをアプリでどのように使用するかを示すことができますか?これをAppクラスまたはどこかから呼び出しますか?
JPM 2015

ありがとうございます!! これまでのテストでうまく機能している
aherrick

この例は不完全です。registerActivityLifecycleCallbacksとは何ですか?
Noman、2015

クラスandroid.app.Applicationのメソッド
rickul

1
よくできました。上に行くために+1してください。完璧なので、他の回答を見ないでください。これは@renoの回答に基づいていますが、実際の例
Stoycho Andreev

63

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

アイデアは、フォアグラウンドでは、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()にも移動しました。また、これは実際のコードというよりはコンセプトの概念だったため、最初の投稿から欠落していたスーパーコールを追加しています。


2
これが最も信頼できる答えですが、onResumeの代わりにonStartを使用しています。
グレッグエニス2014年

オーバーライドされたメソッドにsuper.onResume()およびsuper.onStop()への呼び出しを追加する必要があります。それ以外の場合は、android.app.SuperNotCalledExceptionがスローされます。
Jan Laussmann、2014年

1
私にとっては機能しません...または、少なくともデバイスを回転させているときにイベントを発生させます(これは一種の誤検知です)。
Noya

非常にシンプルで効果的なソリューション!しかし、前のアクティビティの一部を表示できるようにする部分的に透明なアクティビティで動作するかどうかはわかりません。ドキュメントから、onStop is called when the activity is no longer visible to the user
Nicolas Buquet 2015年

3
ユーザーが最初のアクティビティで向きを変更するとどうなりますか?アプリがバックグラウンドになったと報告しますが、これは正しくありません。このシナリオをどのように処理しますか?
Nimrod Dayan

54

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

期待どおりに動作するように見える2つの方法を見つけました。

1つ目は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()でリセットできます。onPostResume()は、アクティビティの作成/再開時に実行される最後のメソッドです。

アプリケーションがバックグラウンドに辞任したことを検出できる場合は、フォアグラウンドに戻ったときにも簡単に検出できます。


18
Googleは、ActivityManager.getRunningTasks()を使用するアプリをおそらく拒否します。ドキュメントは、それは敵の開発目的のみであると述べています。developer.android.com/reference/android/app/...
スカイケルシー


1
これらのアプローチを組み合わせて使用​​する必要があることがわかりました。14でアクティビティを起動するときにonUserLeaveHint()が呼び出されました。 `@Override public void onUserLeaveHint(){inBackground = isApplicationBroughtToBackground(); `
ボートをリスト

7
ユーザーは、強力な権限android.permission.GET_TASKSを使用することにあまり満足しません。
MSquare 2013

6
getRunningTasksはAPIレベル21で廃止されました
Noya

33

拡張するクラスを作成します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
        }
    }

1
以下のためにFragmentActivityあなたはまた、追加したいかもしれませんlevel == ComponentCallbacks2.TRIM_MEMORY_COMPLETEあまりにも。
Srujan Simha

2
このメソッドをポイントしていただきありがとうございます。ユーザーがバックグラウンドのアクティビティを再開するたびにピンダイアログを表示し、このメソッドを使用して設定値を書き込み、baseActivityでこの値を確認する必要があります。
サム・

18

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

19
onUserLeaveHintは、別のアクティビティに移動するときにも呼び出されます
Jonas Stawski 2013

3
たとえば、電話がかかってきて呼び出しアクティビティがアクティブになったときにonUserLeaveHintが呼び出されないため、これには特別なケースがあります。フラグをインテントに追加して、onUserLeaveHint呼び出しを抑制することができるためです。developer.android.com/reference/android/content/...
Groxx

1
また、onResumeがうまく機能しません。試してみましたが、電話がロックされているときにonResumeが呼び出されることがあります。ドキュメントでonResumeの定義を確認すると、次のことがわかります。onResumeは、アクティビティがユーザーに表示されることを示す最良のインジケータではないことに注意してください。キーガードなどのシステムウィンドウが前面にある場合があります。onWindowFocusChanged(boolean)を使用して、ユーザーのアクティビティがユーザーに表示されていることを確認します(たとえば、ゲームを再開するため)。developer.android.com/reference/android/app/...
J-ROU

このソリューション複数activities.Plzがある場合は、フォアグラウンド/バックグラウンドを決定するヘルプが参照していないstackoverflow.com/questions/3667022/...を
ラジTrivediの

14

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

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


11
APIレベル14 = \以降
imort

これはきれいで、私にとってはうまくいくようです。ありがとう
duanbo1983

これは承認された回答とどのように異なりますか?どちらも同じアクティビティライフサイクルに依存していますか?
埼玉県

13

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をご覧ください。


これを次のようにマニフェストに追加します:<application android:name = "。AnotherApp">
Dan Alboteanu

9

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

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

この実装方法の使用を検討します。あるアクティビティから別のアクティビティへの移行には、数ミリ秒しかかかりません。最後のアクティビティが消えた時間に基づいて、特定の戦略でユーザーを再ログインすると見なすことができます。
drindt 2016年

6

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を拡張するようなすべてのアクティビティのスーパークラスとしてこのBaseActivityを使用し、アプリケーションの起動時にonAppStartが呼び出され、アプリケーションが任意の画面からバックグラウンドに移動したときにonAppPause()が呼び出されます。


@kiran boghra:ソリューションに誤検知はありますか?
Harish Vishwakarma 2017

この場合、onStart()およびonStop()関数を使用して問題を完全に解決できます。アプリについて説明します
Pir Fahim Shah

6

これはProcessLifecycleOwnerでかなり簡単です

これらの依存関係を追加する

implementation "android.arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "android.arch.lifecycle:compiler:$project.archLifecycleVersion"

Kotlin

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


5

ライフサイクルオブザーバをアタッチする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()、Applicationクラスのでこれを呼び出します。

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

これであなたはのイベント取り込むことができるようになりますON_PAUSEし、ON_STOPそれがバックグラウンドで行く場合に発生アプリケーションのを。


4

アプリケーション全体がいつバックグラウンド/フォアグラウンドになるかを伝える簡単なライフサイクルメソッドはありません。

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

少しの回避策で、それは可能です。ここで、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つあり、アプリがバックグラウンドにある場合、開始状態のアクティビティはありません。

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

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

    activityReferences開始状態のアクティビティの数を保持します。isActivityChangingConfigurations現在のアクティビティが方向スイッチのように構成変更を行っているかどうかを示すフラグです。

  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ため、アクティビティが構成の変更を通過するシナリオを回避するための追加チェックがあります。


3

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

/**
 * 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

以下を使用できます。

protected void onRestart()

新規の起動と再起動を区別するため。

ここに画像の説明を入力してください


3

編集2:以下に記述した内容は実際には機能しません。Googleは、ActivityManager.getRunningTasks()への呼び出しを含むアプリを拒否しました。ドキュメントからこのAPIはデバッグと開発のみを目的としていることが明らかです。タイマーを使用する新しいスキームでGitHubプロジェクトを更新する時間があるとすぐに、この投稿を更新します。

編集1:ブログ投稿を作成しこれを本当に簡単にするためにシンプルなGitHubリポジトリを作成しまし

承認された回答と最高評価の回答はどちらも、実際には最善の方法ではありません。最高評価の回答のisApplicationBroughtToBackground()の実装は、アプリケーションのメインアクティビティが同じアプリケーションで定義されているアクティビティに委譲しているが、別の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;
}

ちなみに、onStart()でこれを呼び出すと、アラームの発生などで単純なダイアログがスローされたときに呼び出されなくなります。
スカイケルシー

2

ここで正解

以下のような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


1

私のソリューションは、@ 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

私はこれを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

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

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

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

これは、各アクティビティで常に呼び出されます。たとえば、ユーザーのオンラインステータスを検出したい場合、これをどのように使用できますか
Maksim Kniazev 2017

それは質問が望んでいることです。ホーム画面に移動してアクティビティに戻ったときにのみ呼び出されます。
Amir Ziarati 2017

インターネット接続を意味する場合は、必要なときに確認することをお勧めします。APIを呼び出す必要がある場合は、呼び出す直前にインターネット接続を確認してください。
Amir Ziarati 2017

1

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

[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

これは@ d60402の回答の修正版です。 https

そこで言及されているすべてを行います。しかしBase Activity、すべてのアクティビティの親として、それをオーバーライドしてonResume()onPause、下の操作を行います。

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

registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback);

これにcallbackはすべてのアクティビティライフサイクルメソッドがあり、オーバーライドonActivityResumed()してonActivityPaused()

この要旨をご覧くださいhttps : //gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b


1

あなたはの助けを借りて、簡単にこれを達成することができますActivityLifecycleCallbacksし、ComponentCallbacks2以下のようなもの。

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

お役に立てれば。

編集 代替手段として、ライフサイクル対応のアーキテクチャコンポーネントを使用できるようになりました。


1

タイムスタンプをチェックせずにローテーションも処理するアプローチが見つからなかったので、アプリでこれをどのように実行するかも共有したいと思いました。この回答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

私たちは、拡張することができます。このソリューションを使用しました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

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


私はデータベースについて話しませんでした...どういう意味ですか?
Joris Weimar

私はあなたの答えをサポートしています。一時停止呼び出しで、それは良い解決策ではないながら、私たちは..データベースにそのフラグの値を保存することができていても
サンディープP
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.