Androidアプリがフォアグラウンドで実行されているかどうかを確認するにはどうすればよいですか?


83

c2dmによってトリガーされるAndroidアプリでステータスバー通知を行っています。アプリが実行されている場合に通知を表示したくありません。アプリが実行中であり、フォアグラウンドにあるかどうかをどのように判断しますか?



これも同様の質問です... onStart / onStopでフラグを試しましたが、機能しませんでした。停止/開始と一時停止/再開の違いはまだわかりません。
Andrew Thomas

1
このソリューションを使用する必要があります:stackoverflow.com/questions/3667022/…–
Informatic0re


サポートライブラリバージョン26以降、必要なときにいつでもProcessLifecycleOwnerにクエリを実行するだけです。stackoverflow.com/a/52678290/6600000を
KeivanEsbati19年

回答:


55

のようなグローバル変数を作成private boolean mIsInForegroundMode;し、のfalseonPause()とのtrue値を割り当てますonResume()

サンプルコード:

private boolean mIsInForegroundMode;

@Override
protected void onPause() {
    super.onPause();
    mIsInForegroundMode = false;
}

@Override
protected void onResume() {
    super.onResume();
    mIsInForegroundMode = true;
}

// Some function.
public boolean isInForeground() {
    return mIsInForegroundMode;
}

2
@MurVotema:そうだね。ただし、変更が発生するたびに、この変数を設定やデータベースなどに自由に渡すことができます。
wroclai 2012

19
@Shellyしかし、同じアプリ内のアクティビティを切り替えると、変数はTrue / False / Trueになります。これは、アプリがフォアグラウンドを失ったことを実際に検出するには、ヒステリシスが必要であることを意味します。
ラドゥ2012

10
これは最善の解決策ではありません。以下の@Gadenkanソリューションを確認してください。
フェリペ・リマ

単純なシナリオでは機能しますが、アクティビティにonActivityResult()があると、状況が複雑になります。アプリのすべてのアクティビティがバックグラウンドで実行されている場合にのみバックグラウンドサービスを実行したいのですが、上記のソリューションでは不十分です。
Josh

1
@Timaすべてのアクティビティで利用できるようにする場合は、新しいMyOwnActivity extends AppCompatActivityを作成できます...次に、MyOwnActivity内で、onResume()/ onPause()の両方をオーバーライドし、最後に、代わりにMyOwnActivityからすべてのアプリアクティビティを拡張します。 AppCompatActivity。問題が解決しました。; )
2017

112

または、メソッドActivityManagerごとに実行されているタスクを確認することもできますgetRunningTasks。次に、返されたタスクのリストの最初のタスク(フォアグラウンドのタスク)を確認します(それが自分のタスクである場合)。
コード例は次のとおりです。

public Notification buildNotification(String arg0, Map<String, String> arg1) {

    ActivityManager activityManager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> services = activityManager
            .getRunningTasks(Integer.MAX_VALUE);
    boolean isActivityFound = false;

    if (services.get(0).topActivity.getPackageName().toString()
            .equalsIgnoreCase(appContext.getPackageName().toString())) {
        isActivityFound = true;
    }

    if (isActivityFound) {
        return null;
    } else {
        // write your code to build a notification.
        // return the notification you built here
    }

}

また、上記のコードでメソッドを実行できるようにするにGET_TASKSは、manifest.xmlファイルに権限を追加することを忘れないでくださいgetRunningTasks()

<uses-permission android:name="android.permission.GET_TASKS" />

p / s:この方法で同意する場合、この許可は現在非推奨であることに注意してください。


24
このコードを使用する場合は、<uses-permission android:name = "android.permission.GET_TASKS" />を追加することを忘れないでください
Lakshmanan 2012

13
Nitpicks:toString()によって返される文字列の呼び出しgetPackageName()は冗長です。また、によって返される最初のタスクのみに関心があるため、の代わりにgetRunningTasks()渡すことができます。1Integer.MAX_VALUE
Jonik 2013年

14
注:このメソッドは、タスク管理ユーザーインターフェイスのデバッグと表示のみを目的としています。ここにある情報に基づいて異なる動作を決定するなど、アプリケーションのコアロジックにこれを使用しないでください。このような使用はサポートされておらず、将来的に機能しなくなる可能性があります。たとえば、複数のアプリケーションを同時にアクティブに実行できる場合、制御フローの目的でここでデータの意味について行われた仮定は正しくありません。
Informatic0re

5
残念ながら、getRunningTasks()はAndroid L(API 20)以降非推奨になっています。L以降、この方法はサードパーティのアプリケーションでは使用できなくなりました。ドキュメント中心の最近の導入により、発信者に個人情報が漏洩する可能性があります。下位互換性のために、データの小さなサブセットを返します。少なくとも呼び出し元自身のタスクと、機密性が低いことがわかっているホームなどの他のタスクです。
サム・呂

2
おそらく、発信者のタスクのみが表示されている場合、最初のタスクと上位のアクティビティは常にあなたのものになります。つまり、これは常に真のポストロリポップを返します。
マイクホラー

46

これはかなり古い投稿ですが、それでもかなり関連性があります。上記の受け入れられた解決策は機能するかもしれませんが、間違っています。ダイアンハックボーンが書いたように:

これらのAPIは、アプリケーションがUIフローのベースにするためのものではなく、実行中のアプリやタスクマネージャーなどをユーザーに表示するためのものです。

はい、これらのもののためにメモリに保持されているリストがあります。ただし、別のプロセスではオフになっており、スレッドとは別に実行されているスレッドによって管理されており、(a)正しい決定を下すために時間内に確認したり、(b)戻ったときまでに一貫した状況を把握したりすることはできません。さらに、どの「次の」アクティビティに進むかについての決定は、切り替えが行われる時点で常に行われ、その正確な時点(切り替えを行うためにアクティビティ状態が一時的にロックダウンされる)までは行われません。そのようなことで次のことがどうなるかを実際に知っています。

また、ここでの実装とグローバルな動作は、将来同じであるとは限りません。

正しい解決策は、ActivityLifeCycleCallbacksを実装することです。

これには基本的にアプリケーションクラスが必要であり、そこにハンドラーを設定して、アプリでのアクティビティの状態を識別できます。


7
これはそれを使用する方法の例ですbaroqueworksdev.blogspot.com/2012/12/…–
JK

2
これが最善の解決策であり、おそらく将来的には壊れないだろうと思います。なぜ十分な票を獲得できなかったのだろうか。
Rohan Kandwal 2015年

2
サンプルコードを書くと、例なしで答えをスキップする人がいるため、より多くの票を獲得できます...しかし、それが最善の解決策です...
Saman Salehi 2016年

これは、受け入れられた回答で使用されているオーバーライドonPauseおよびonResume方法とどのように異なりますか?
埼玉

24

Vinayが言うように、おそらく最良の解決策(新しいAndroidバージョン、14以降をサポートするため)はActivityLifecycleCallbacksApplicationクラスの実装で使用することです。

package com.telcel.contenedor.appdelegate;

import android.app.Activity;
import android.app.Application.ActivityLifecycleCallbacks;
import android.os.Bundle;

/** Determines global app lifecycle states. 
 * 
 * The following is the reference of activities states:
 * 
 * The <b>visible</b> lifetime of an activity happens between a call to onStart()
 * until a corresponding call to onStop(). During this time the user can see the
 * activity on-screen, though it may not be in the foreground and interacting with 
 * the user. The onStart() and onStop() methods can be called multiple times, as 
 * the activity becomes visible and hidden to the user.
 * 
 * The <b>foreground</b> lifetime of an activity happens between a call to onResume()
 * until a corresponding call to onPause(). During this time the activity is in front
 * of all other activities and interacting with the user. An activity can frequently
 * go between the resumed and paused states -- for example when the device goes to
 * sleep, when an activity result is delivered, when a new intent is delivered -- 
 * so the code in these methods should be fairly lightweight. 
 * 
 * */
public class ApplicationLifecycleManager implements ActivityLifecycleCallbacks {

    /** Manages the state of opened vs closed activities, should be 0 or 1. 
     * It will be 2 if this value is checked between activity B onStart() and
     * activity A onStop().
     * It could be greater if the top activities are not fullscreen or have
     * transparent backgrounds.
     */
    private static int visibleActivityCount = 0;

    /** Manages the state of opened vs closed activities, should be 0 or 1
     * because only one can be in foreground at a time. It will be 2 if this 
     * value is checked between activity B onResume() and activity A onPause().
     */
    private static int foregroundActivityCount = 0;

    /** Returns true if app has foreground */
    public static boolean isAppInForeground(){
        return foregroundActivityCount > 0;
    }

    /** Returns true if any activity of app is visible (or device is sleep when
     * an activity was visible) */
    public static boolean isAppVisible(){
        return visibleActivityCount > 0;
    }

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

    public void onActivityDestroyed(Activity activity) {
    }

    public void onActivityResumed(Activity activity) {
        foregroundActivityCount ++;
    }

    public void onActivityPaused(Activity activity) {
        foregroundActivityCount --;
    }


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

    public void onActivityStarted(Activity activity) {
        visibleActivityCount ++;
    }

    public void onActivityStopped(Activity activity) {
        visibleActivityCount --;
    }
}

そして、アプリケーションのonCreate()方法で:

registerActivityLifecycleCallbacks(new ApplicationLifecycleManager());

次に、ApplicationLifecycleManager.isAppVisible()またはApplicationLifecycleManager.isAppInForeground()を使用して、目的の状態を確認します。


isAppVisible()でのみ機能します。アプリを閉じた場合、フォアグラウンドに送信しても効果はありません
AlwaysConfused 2017年

19

API 16以降、次のように実行できます。

static boolean shouldShowNotification(Context context) {
    RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
    ActivityManager.getMyMemoryState(myProcess);
    if (myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND)
        return true;

    KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
    // app is in foreground, but if screen is locked show notification anyway
    return km.inKeyguardRestrictedInputMode();
}

効率的なソリューション。ありがとう:)
Md。SajedulKarim2017

とても素敵な解決策。権限は必要ありません。
user3144836 2017

驚くばかり!私はタスクリストを使用していましたが、GET TASKが必要です..本当に感謝しています!
ヘキサゴッド

15

参考までに、Gadenkanソリューションを使用する場合(これは素晴らしいです!!)、追加することを忘れないでください

<uses-permission android:name="android.permission.GET_TASKS" />

マニフェストに。


9
Android Lollipopでは、この権限は廃止されました
Mussa

14

Gadenkanのソリューションのわずかにクリーンアップされたバージョン。任意のアクティビティ、またはすべてのアクティビティの基本クラスを配置します。

protected boolean isRunningInForeground() {
    ActivityManager manager = 
         (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningTaskInfo> tasks = manager.getRunningTasks(1);
    if (tasks.isEmpty()) {
        return false;
    }
    String topActivityName = tasks.get(0).topActivity.getPackageName();
    return topActivityName.equalsIgnoreCase(getPackageName());
}

を呼び出すことができるようにするにはgetRunningTasks()、これをあなたのAndroidManifest.xml:に追加する必要があります

<uses-permission android:name="android.permission.GET_TASKS"/>

ただし、ActivityManager.getRunningTasks() Javadocの内容に注意してください。

注:このメソッドは、タスク管理ユーザーインターフェイスのデバッグと表示のみを目的としています。ここにある情報に基づいて異なる動作を決定するなど、アプリケーションのコアロジックにこれを使用しないでください。このような使用はサポートされておらず、将来的に機能しなくなる可能性があります。

更新(2015年2月)

APIレベル21で非推奨になったことgetRunningTasks()に注意してください!

現在LOLLIPOP、この方法はサードパーティのアプリケーションでは使用できません。ドキュメント中心の最近の導入により、発信者に個人情報が漏洩する可能性があります。下位互換性のために、データの小さなサブセットを返します。少なくとも呼び出し元自身のタスクと、機密性が低いことがわかっているホームなどの他のタスクです。

したがって、私が以前に書いたことはさらに関連性があります。

多くの場合、おそらくより良い解決策を思い付くことができます。たとえば、onPause()およびonResume()、おそらくすべてのアクティビティのBaseActivityで何かを実行します。

(私たちの場合、フォアグラウンドにいない場合にオフラインアラートアクティビティを起動したくないので、BaseActivityでは、「オフラインになりました」信号をリッスンしてonPause()いるRxJavaから単にサブスクライブを解除しSubscriptionます。)


使用したRxJavaコードを投稿していただけますか?私はRxJavaを使いたいのですが、現時点ではRxJavaの学習に多くの時間を費やしたくありません:(
MBH

@MBH:RxJavaを使用することは、基本的な概念に慣れるために時間を費やさなければ、おそらくあまり実り多いものではありません... :-)いずれにせよ、ここに小さな例があります。
Jonik 2016年

9

Gadenkanの返信をフォローアップするために、アプリがフォアグラウンドで実行されていないかどうかを確認できるように、このようなものが必要でしたが、アプリ全体でフラグを設定/設定解除する必要のないものが必要でした。

Gadenkanのコードはほとんど頭に釘付けになりましたが、それは私自身のスタイルではなく、よりきれいにできると感じたので、私のアプリではこれに凝縮されています。

if (!context.getPackageName().equalsIgnoreCase(((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getRunningTasks(1).get(0).topActivity.getPackageName()))
{
// App is not in the foreground
}

(補足:チェックを逆に機能させたい場合は、!を削除するだけです)

このアプローチではGET_TASKS許可が必要ですが。


3

サポートライブラリのバージョン26の起動に使用できるProcessLifecycleOwnerを説明したように、ちょうどあなたの依存関係に追加し、アプリの現在の状態を判断するために、ここで例えば、:

dependencies {
    def lifecycle_version = "1.1.1"

    // ViewModel and LiveData
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
    // alternatively - Lifecycles only (no ViewModel or LiveData).
    //     Support library depends on this lightweight import
    implementation "android.arch.lifecycle:runtime:$lifecycle_version"
    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" // use kapt for Kotlin
}

ProcessLifecycleOwnerアプリの状態を確認したいときはいつでもクエリを実行できるようになりました。たとえば、アプリがフォアグラウンドで実行されているかどうかを確認するには、次のようにする必要があります。

 boolean isAppInForeground = ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
 if(!isAppInForeground)
    //Show Notification in status bar

1
これが正解です。他のものの多くは非推奨と考えるべきである
11m0

AndroidX Javaの場合implementation 'androidx.lifecycle:lifecycle-process:2.2.0'、プロジェクトGradleで使用します。
ジョナサン

1

さまざまな回答とコメントに基づいて、ヘルパークラスに追加できるインラインバージョンを次に示します。

public static boolean isAppInForeground(Context context) {
  List<RunningTaskInfo> task =
      ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE))
          .getRunningTasks(1);
  if (task.isEmpty()) {
    return false;
  }
  return task
      .get(0)
      .topActivity
      .getPackageName()
      .equalsIgnoreCase(context.getPackageName());
}

他の回答で述べたように、次の権限をに追加する必要がありますAndroidManifest.xml

<uses-permission android:name="android.permission.GET_TASKS"/>

Android Lollipopでは、この権限は廃止されました
Mussa

1

これを行うためのより安全な方法は、通知を作成する前にアプリがバックグラウンドにあるかどうかを確認するよりも、ブロードキャストレシーバーのonPause()とonResume()をそれぞれ無効または有効にすることです。

この方法では、実際のアプリケーションロジックをより細かく制御でき、将来変更される可能性はありません。

@Override
protected void onPause() {
    unregisterReceiver(mHandleMessageReceiver);
    super.onPause();
}

@Override
protected void onResume() {
    super.onResume();
    registerReceiver(mHandleMessageReceiver, new IntentFilter(DISPLAY_MESSAGE_ACTION));
}

1

アクティビティをブール値にマッピングすることで、アプリケーションがフォアグラウンドかバックグラウンドかを確認するためのより簡単で正確な方法を見つけました。

ここで完全な要点を確認してください


1

これは、@ user2690455によって上記で説明された素晴らしい単純なソリューションのコードです。少し冗長に見えますが、全体的にはかなり軽量であることがわかります。

私の場合、AppCompatActivityも使用しているため、2つの基本クラスが必要でした。

public class BaseActivity extends Activity {

    /**
     * Let field be set only in base class
     * All callers must use accessors,
     * and then it's not up to them to manage state.
     *
     * Making it static since ..
     * 1. It needs to be used across two base classes
     * 2. It's a singleton state in the app
     */
    private static boolean IS_APP_IN_BACKGROUND = false;

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

        BaseActivity.onResumeAppTracking(this);

        BaseActivity.setAppInBackgroundFalse();
    }

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

        BaseActivity.setAppInBackgroundTrue();
    }

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

        BaseActivity.setAppInBackgroundFalse();
    }

    protected static void onResumeAppTracking(Activity activity) {

        if (BaseActivity.isAppInBackground()) {

            // do requirements for returning app to foreground
        }

    }

    protected static void setAppInBackgroundFalse() {

        IS_APP_IN_BACKGROUND = false;
    }

    protected static void setAppInBackgroundTrue() {

        IS_APP_IN_BACKGROUND = true;
    }

    protected static boolean isAppInBackground() {

        return IS_APP_IN_BACKGROUND;
    }
}

1

これは、アクティビティの開始時に何らかのアクションを実行し、アプリがフォアグラウンドかバックグラウンドかを確認する場合にのみ役立ちます。

アクティビティマネージャーを使用する代わりに、コードを介して実行できる簡単なトリックがあります。活動サイクルを注意深く観察すると、2つの活動と前景から背景への流れは次のようになります。AとBが2つのアクティビティであるとします。

AからBへの移行時:1。AのonPause()が呼び出されます2. BのonResume()が呼び出されます3. Bが完全に再開されたときにAのonStop()が呼び出されます

アプリがバックグラウンドに入ると:1。AのonPause()が呼び出されます2. AのonStop()が呼び出されます

アクティビティにフラグを設定するだけで、バックグラウンドイベントを検出できます。

抽象アクティビティを作成し、他のアクティビティから拡張して、バックグラウンドイベントが必要な場所で他のすべてのアクティビティのコードをコピーして貼り付ける必要がないようにします。

抽象アクティビティでは、フラグisAppInBackgroundを作成します。

onCreate()メソッドの場合:isAppInBackground = false;

onPause()メソッドの場合:isAppInBackground = false;

onStop()メソッドの場合:isAppInBackground = true;

isAppInBackgroundがtrueの場合は、onResume()をチェックインする必要があります。nフラグを確認した後、もう一度isAppInBackground = falseを設定します。

最初のonSTop()は常に2番目のアクティビティが再開した後に呼び出されるため、2つのアクティビティ間の移行では、フラグがtrueになることはなく、アプリがバックグラウンドの場合、アクティビティのonStop()はonPauseの直後に呼び出されるため、フラグは次の場合にtrueになります。後でアプリを開きます。

ただし、このアプローチにはもう1つのシナリオがあります。アプリの画面のいずれかがすでに開いていて、モバイルをアイドル状態にすると、しばらくするとモバイルはスリープモードになり、モバイルのロックを解除すると、バックグラウンドイベントで処理されます。


1

これが私が使用するメソッド(およびサポートメソッド)です:

private boolean checkIfAppIsRunningInForeground() {
    ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
    for(ActivityManager.RunningAppProcessInfo appProcessInfo : activityManager.getRunningAppProcesses()) {
        if(appProcessInfo.processName.contains(this.getPackageName())) {
            return checkIfAppIsRunningInForegroundByAppImportance(appProcessInfo.importance);
        }
    }
    return false;
}

private boolean checkIfAppIsRunningInForegroundByAppImportance(int appImportance) {
    switch (appImportance) {
        //user is aware of app
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND:
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE:
            return true;
        //user is not aware of app
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND:
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY:
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE:
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE:
        default:
            return false;
    }
}

2年前に投稿されたソリューションに投票できなくなったようです。この方法は、Androidでさまざまなセキュリティアップデートが行われた後は機能しなくなりました。
ドロイドクリス

0

これに対するグローバルコールバックはありませんが、アクティビティごとにonStop()です。アトミックintをいじる必要はありません。開始されたアクティビティの数を含むグローバルintを作成し、すべてのアクティビティでonStart()でインクリメントし、onStop()でデクリメントします。

これに従ってください


0
     public static boolean isAppRunning(Context context) {

 // check with the first task(task in the foreground)
 // in the returned list of tasks

   ActivityManager activityManager = (ActivityManager)
   context.getSystemService(Context.ACTIVITY_SERVICE);
 List<RunningTaskInfo> services =
 activityManager.getRunningTasks(Integer.MAX_VALUE);
     if
     (services.get(0).topActivity.getPackageName().toString().equalsIgnoreCase(context.getPackageName().toString()))
     {
     return true;
     }
     return false;
     }

getRunningTasksはもうオプションではありません-新しいセキュリティモデルはこれをほとんど役に立たなくし、非推奨になりました。
トニーマロ

@AnaghHegdeシステムから実行中のアクティビティのリストを取得して、Gadenkanによる上記の回答を参照してください。
トニー・マロ

0

ここで説明した以前のアプローチは最適ではありません。タスクベースのアプローチには、望ましくない可能性のある権限が必要であり、「ブール」アプローチでは、同時変更が混乱する傾向があります。

私が使用しているアプローチと(私が信じている)ほとんどの場合、非常にうまく機能します。

で活動回数を追跡する「MainApplication」クラス持っているのAtomicIntegerを

import android.app.Application;

import java.util.concurrent.atomic.AtomicInteger;

public class MainApplication extends Application {
    static class ActivityCounter {
        private static AtomicInteger ACTIVITY_COUNT = new AtomicInteger(0);

        public static boolean isAppActive() {
            return ACTIVITY_COUNT.get() > 0;
        }

        public static void activityStarted() {
            ACTIVITY_COUNT.incrementAndGet();
        }

        public static void activityStopped() {
            ACTIVITY_COUNT.decrementAndGet();
        }
    }
}

そして、他のアクティビティが拡張する基本アクティビティクラスを作成します。

import android.app.Activity;
import android.support.annotation.CallSuper;

public class TestActivity extends Activity {
    @Override
    @CallSuper
    protected void onStart() {
        MainApplication.ActivityCounter.activityStarted();
        super.onStart();
    }

    @Override
    @CallSuper
    protected void onStop() {
        MainApplication.ActivityCounter.activityStopped();
        super.onStop();
    }
}

わかりました...しかし、ACTIVITY_COUNT.decrementAndGet();の使用を検討してください。activityStopped()で。
ChrisCarneiro 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.