バックグラウンドタスクまたはサービスから現在のフォアグラウンドアプリケーションを特定する


112

組み込みのアプリケーション(メッセージング、連絡先など)がいつ実行されているかを認識する、バックグラウンドで実行される1つのアプリケーションが欲しいです。

だから私の質問は:

  1. アプリケーションをバックグラウンドで実行する方法。

  2. 私のバックグラウンドアプリケーションが、フォアグラウンドで現在実行されているアプリケーションを知る方法。

経験のある方からの回答をお待ちしています。


あなたがやろうとしていることを十分に説明していないと思います。バックグラウンドアプリケーションは何をしようとしていますか?どのようにしてユーザーと対話できますか?現在のフォアグラウンドアプリが何であるかを知る必要があるのはなぜですか?等
チャールズ・ダフィー


1
フォアグラウンドアプリを検出するには、github.com
ricvalerio / foregroundappcheckerを

回答:


104

「2.バックグラウンドアプリケーションがフォアグラウンドで現在実行されているアプリケーションをどのようにして知ることができるか」について。

getRunningAppProcesses()メソッドを使用しないでください。これは、私の経験からあらゆる種類のシステムのゴミを返し、複数の結果が得られるためRunningAppProcessInfo.IMPORTANCE_FOREGROUNDです。getRunningTasks()代わりに使用

これは、現在のフォアグラウンドアプリケーションを識別するためにサービスで使用するコードで、非常に簡単です。

ActivityManager am = (ActivityManager) AppService.this.getSystemService(ACTIVITY_SERVICE);
// The first in the list of RunningTasks is always the foreground task.
RunningTaskInfo foregroundTaskInfo = am.getRunningTasks(1).get(0);

それで、フォアグラウンドのアプリ/アクティビティの詳細に簡単にアクセスできます。

String foregroundTaskPackageName = foregroundTaskInfo .topActivity.getPackageName();
PackageManager pm = AppService.this.getPackageManager();
PackageInfo foregroundAppPackageInfo = pm.getPackageInfo(foregroundTaskPackageName, 0);
String foregroundTaskAppName = foregroundAppPackageInfo.applicationInfo.loadLabel(pm).toString();

これには、活動の目録で追加の許可が必要であり、完全に機能します。

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

1
これは正解としてマークする必要があります。必要な権限:android.permission.GET_TASKSは、それを回避できる他のメソッドの唯一のマイナーな欠点です。
ブランドール2013年

3
上記に編集:注:このメソッドは、タスク管理ユーザーインターフェイスのデバッグと表示のみを目的としています。<-ちょうどそれをJavaドキュメントで見つけました。したがって、この方法は将来予告なしに変更される可能性があります。
brandall 2013年

47
注:getRunningTasks()API 21で廃止され(ロリポップ) - developer.android.com/reference/android/app/...
dtyler

@ dtyler、OK。代わりに他の方法はありますか?Googleはサードパーティのアプリが機密情報を取得することを望んでいませんが、デバイスのプラットフォームキーを持っています。システム権限を持っている場合、他の方法で同じことはできますか?
Yeung

21で導入された「使用統計api」を使用する方法があります
KunalK

38

私は正しい方法を難しい方法で見つけなければなりませんでした。以下のコードはcyanogenmod7(タブレットの微調整)の一部であり、android 2.3.3 / gingerbreadでテストされています。

メソッド:

  • getForegroundApp-フォアグラウンドアプリケーションを返します。
  • getActivityForApp-見つかったアプリのアクティビティを返します。
  • isStillActive-以前に見つかったアプリがまだアクティブアプリであるかどうかを判断します。
  • isRunningService-getForegroundAppのヘルパー関数

これはうまくいけばすべての点でこの問題に答えます(:

private RunningAppProcessInfo getForegroundApp() {
    RunningAppProcessInfo result=null, info=null;

    if(mActivityManager==null)
        mActivityManager = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
    List <RunningAppProcessInfo> l = mActivityManager.getRunningAppProcesses();
    Iterator <RunningAppProcessInfo> i = l.iterator();
    while(i.hasNext()){
        info = i.next();
        if(info.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND
                && !isRunningService(info.processName)){
            result=info;
            break;
        }
    }
    return result;
}

private ComponentName getActivityForApp(RunningAppProcessInfo target){
    ComponentName result=null;
    ActivityManager.RunningTaskInfo info;

    if(target==null)
        return null;

    if(mActivityManager==null)
        mActivityManager = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
    List <ActivityManager.RunningTaskInfo> l = mActivityManager.getRunningTasks(9999);
    Iterator <ActivityManager.RunningTaskInfo> i = l.iterator();

    while(i.hasNext()){
        info=i.next();
        if(info.baseActivity.getPackageName().equals(target.processName)){
            result=info.topActivity;
            break;
        }
    }

    return result;
}

private boolean isStillActive(RunningAppProcessInfo process, ComponentName activity)
{
    // activity can be null in cases, where one app starts another. for example, astro
    // starting rock player when a move file was clicked. we dont have an activity then,
    // but the package exits as soon as back is hit. so we can ignore the activity
    // in this case
    if(process==null)
        return false;

    RunningAppProcessInfo currentFg=getForegroundApp();
    ComponentName currentActivity=getActivityForApp(currentFg);

    if(currentFg!=null && currentFg.processName.equals(process.processName) &&
            (activity==null || currentActivity.compareTo(activity)==0))
        return true;

    Slog.i(TAG, "isStillActive returns false - CallerProcess: " + process.processName + " CurrentProcess: "
            + (currentFg==null ? "null" : currentFg.processName) + " CallerActivity:" + (activity==null ? "null" : activity.toString())
            + " CurrentActivity: " + (currentActivity==null ? "null" : currentActivity.toString()));
    return false;
}

private boolean isRunningService(String processname){
    if(processname==null || processname.isEmpty())
        return false;

    RunningServiceInfo service;

    if(mActivityManager==null)
        mActivityManager = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
    List <RunningServiceInfo> l = mActivityManager.getRunningServices(9999);
    Iterator <RunningServiceInfo> i = l.iterator();
    while(i.hasNext()){
        service = i.next();
        if(service.process.equals(processname))
            return true;
    }

    return false;
}

1
私に質問しても構わないのであれば、サービスが実行されているかどうか、まだアクティブであるかどうか、およびフォアグラウンドアプリケーションを取得するためだけにアクティビティを取得するためのメソッドが必要なのはなぜですか。

2
申し訳ありませんが、機能していません。一部のアプリは理由もなくフィルタリングされます。サービスを実行するときだと思います。そのため、isRunningServiceがそれらを追い出します。
2012

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

誰かがこのアプローチが単に行うよりも優れていることを知っていmActivityManager.getRunningTasks(1).get(0).topActivityますか?
サム

35

次のコードを試してください:

ActivityManager activityManager = (ActivityManager) newContext.getSystemService( Context.ACTIVITY_SERVICE );
List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
for(RunningAppProcessInfo appProcess : appProcesses){
    if(appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND){
        Log.i("Foreground App", appProcess.processName);
    }
}

プロセス名は、フォアグラウンドで実行されているアプリのパッケージ名です。それをアプリケーションのパッケージ名と比較してください。同じ場合、アプリケーションはフォアグラウンドで実行されています。

これがあなたの質問に答えてくれることを願っています。


7
AndroidバージョンL以降、他のソリューションで使用されていたメソッドが廃止されたため、これが機能する唯一のソリューションです。
androidGuy 2014年

4
ただし、アプリがフォアグラウンドで画面がロックされている場合は機能しません
Krit

正解はチェックする必要があります
A_rmas

1
承認された回答のような追加の許可が必要ですか?
wonsuc 2017

1
これは正確ではありません。プロセスの名前は、対応するアプリパッケージの名前と常に同じであるとは限りません。
サム

25

ロリポップ以降、これは変更されました。ユーザーが[設定]-> [セキュリティ]->(最後にスクロールダウン)にアクセスする前に、以下のコードを見つけてください。

private void printForegroundTask() {
    String currentApp = "NULL";
    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        UsageStatsManager usm = (UsageStatsManager) this.getSystemService(Context.USAGE_STATS_SERVICE);
        long time = System.currentTimeMillis();
        List<UsageStats> appList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,  time - 1000*1000, time);
        if (appList != null && appList.size() > 0) {
            SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
            for (UsageStats usageStats : appList) {
                mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
            }
            if (mySortedMap != null && !mySortedMap.isEmpty()) {
                currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
            }
        }
    } else {
        ActivityManager am = (ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> tasks = am.getRunningAppProcesses();
        currentApp = tasks.get(0).processName;
    }

    Log.e(TAG, "Current App in foreground is: " + currentApp);
}

1
この使用統計はプログラムで設定できませんか?
rubmz

@rubmzこれに関する解決策はありますか?
VVB 2016年

1
私のdoogeeとandroid 5.1では、「アプリにアクセス許可を与える」というオプションはありません
djdance

これは100%正確な結果を生成しません...それはほとんどの場合正しいです
CamHart

1
これは、最後のアプリをフォアグラウンドで表示しませんが、おそらく最後に「使用」された方法である可能性があります-アクティビティを離れて別のアクティビティに移動し、上からランチャーメニューをプルしてリリースすると、「あなたが別のものに行くまで、間違った」活動。画面をスクロールするかどうかは関係ありません。別のアクティビティを起動するまで、間違ったアクティビティが報告されます。
Azurlake

9

アプリがフォアグラウンドにあるかどうかを独自のサービス/バックグラウンドスレッドから確認する必要がある場合。これは私がそれを実装した方法であり、私にとってはうまくいきます:

public class TestApplication extends Application implements Application.ActivityLifecycleCallbacks {

    public static WeakReference<Activity> foregroundActivityRef = null;

    @Override
    public void onActivityStarted(Activity activity) {
        foregroundActivityRef = new WeakReference<>(activity);
    }

    @Override
    public void onActivityStopped(Activity activity) {
        if (foregroundActivityRef != null && foregroundActivityRef.get() == activity) {
            foregroundActivityRef = null;
        }
    }

    // IMPLEMENT OTHER CALLBACK METHODS
}

次に、アプリがフォアグラウンドにあるかどうかに関係なく、他のクラスからチェックするには、次を呼び出すだけです

if(TestApplication.foregroundActivityRef!=null){
    // APP IS IN FOREGROUND!
    // We can also get the activity that is currently visible!
}

更新(SHSで指摘):

ApplicationクラスのonCreateメソッドでコールバックを登録することを忘れないでください。

@Override
public void onCreate() {
    ...
    registerActivityLifecycleCallbacks(this);
}

2
それは私が過去2時間以来探していたものです。ありがとうございました!:)
waseefakhtar

1
「registerActivityLifecycleCallbacks(this);」を呼び出す必要があります。アプリケーションクラス(TestApplication)のオーバーライドメソッド "onCreate()"内。これにより、アプリケーションクラスでコールバックが開始されます。
SHS

@SarthakMittal、デバイスがスタンバイモードになった場合、またはデバイスがロックされた場合、onActivityStopped()が呼び出されます。コードに基づいて、これはアプリケーションがバックグラウンドにあることを示します。しかし、実際には前景にあります。
Ayaz Alifov

@AyazAlifov電話がスタンバイ状態またはロックされている場合、フォアグラウンドにあるとは見なしませんが、やはり好みによって異なります。
Sarthak Mittal

8

フォアグラウンドアプリケーションを特定するには、フォアグラウンドアプリの検出に使用できます。https://github.com/ricvalerio/foregroundappcheckerを使用できます。デバイスのAndroidバージョンに応じて、さまざまな方法を使用します。

サービスに関しては、レポはそれに必要なコードも提供します。基本的に、android studioにサービスを作成させ、onCreateでappCheckerを使用するスニペットを追加します。ただし、許可を要求する必要があります。


パーフェクト!! Android 7でテスト済み
Pratikタンク2018

本当にありがとう。これが、アプリの通知で直面していた問題を解決した唯一の答えです。通知がポップアップ表示されると、他のすべての回答により、通知を送信したアプリのパッケージ名が返されます。LollipopDetectorがこの問題を解決しました。1つのヒント:API 29以降、MOVE_TO_FOREGROUNDはUsageEvents.Eventで非推奨になっているため、Android Q以降では、ACTIVITY_RESUMEDを使用する必要があります。それからそれは魅力のように働きます!
Jorn Rigter

8

getRunningTasks()非推奨でgetRunningAppProcesses()信頼性がないことを考慮して、StackOverflowで言及されている2つのアプローチを組み合わせることにしました。

   private boolean isAppInForeground(Context context)
    {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
        {
            ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
            ActivityManager.RunningTaskInfo foregroundTaskInfo = am.getRunningTasks(1).get(0);
            String foregroundTaskPackageName = foregroundTaskInfo.topActivity.getPackageName();

            return foregroundTaskPackageName.toLowerCase().equals(context.getPackageName().toLowerCase());
        }
        else
        {
            ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
            ActivityManager.getMyMemoryState(appProcessInfo);
            if (appProcessInfo.importance == IMPORTANCE_FOREGROUND || appProcessInfo.importance == IMPORTANCE_VISIBLE)
            {
                return true;
            }

            KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
            // App is foreground, but screen is locked, so show notification
            return km.inKeyguardRestrictedInputMode();
        }
    }

4
廃止予定の権限<uses-permission android:name = "android.permission.GET_TASKS" />をマニフェストに追加するには、言及する必要があります。そうしないと、アプリがクラッシュします
RJFares

1
android.permission.GET_TASKS-Playストアでは現在禁止されています
Duna


1

これでうまくいきました。ただし、メインメニュー名のみが表示されます。つまり、ユーザーが[設定]-> [Bluetooth]-> [デバイス名]画面を開いた場合、RunningAppProcessInfoはそれを単なる設定として呼び出します。furthurをドリルダウンできません

ActivityManager activityManager = (ActivityManager) context.getSystemService( Context.ACTIVITY_SERVICE );
                PackageManager pm = context.getPackageManager();
                List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
                for(RunningAppProcessInfo appProcess : appProcesses) {              
                    if(appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                        CharSequence c = pm.getApplicationLabel(pm.getApplicationInfo(appProcess.processName, PackageManager.GET_META_DATA));
                        Log.i("Foreground App", "package: " + appProcess.processName + " App: " + c.toString());
                    }               
                }

4
これは自分のアプリがフォアグラウンドで実行されている場合にのみ機能すると思います。他のアプリがフォアグラウンドにある場合は何も報告しません。
アリス

0

このようなことをしてください:

int showLimit = 20;

/* Get all Tasks available (with limit set). */
ActivityManager mgr = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> allTasks = mgr.getRunningTasks(showLimit);
/* Loop through all tasks returned. */
for (ActivityManager.RunningTaskInfo aTask : allTasks) 
{                  
    Log.i("MyApp", "Task: " + aTask.baseActivity.getClassName()); 
    if (aTask.baseActivity.getClassName().equals("com.android.email.activity.MessageList")) 
        running=true;
}

Googleは、ActivityManager.getRunningTasks()を使用するアプリをおそらく拒否します。それが唯一の敵のdevの目的であることをドキュメントの状態:developer.android.com/reference/android/app/... intialコメントを参照してください:stackoverflow.com/questions/4414171/...
ForceMagic

3
@ForceMagic-Googleは、信頼性の低い API を使用するためだけにアプリを拒否することはありません。さらに、GoogleはAndroidアプリケーションの唯一の配布チャネルではありません。とはいえ、このAPIからの情報は信頼できないことに注意してください。
Chris Stratton

@ChrisStratton興味深いですが、あなたはおそらく正しいでしょう。コアロジックに使用することはお勧めしませんが、アプリを拒否しません。コメントリンクからSky Kelseyに警告することもできます。
ForceMagic 2014年

3
「getRunningTasks」はAPI 21以降で機能するので、LOLLIPOPで機能する別の方法を考えた方がいいかもしれません(私自身はこれを自分で
考え出せ

0

ロリポップ以上:

メインフェストに追加:

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

そして、このようなことをしてください:

if( mTaskId < 0 )
{
    List<AppTask> tasks = mActivityManager.getAppTasks(); 
    if( tasks.size() > 0 )
        mTaskId = tasks.get( 0 ).getTaskInfo().id;
}

4
マシュマロに非推奨
jinkal

0

これは、アプリがフォアグラウンドにあるかどうかを確認する方法です。公式のAndroidの提案に従ってAsyncTaskを使用していることに注意してくださいドキュメントで

`

    private class CheckIfForeground extends AsyncTask<Void, Void, Void> {
    @Override
    protected Void doInBackground(Void... voids) {

        ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
        for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
            if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                Log.i("Foreground App", appProcess.processName);

                if (mContext.getPackageName().equalsIgnoreCase(appProcess.processName)) {
                    Log.i(Constants.TAG, "foreground true:" + appProcess.processName);
                    foreground = true;
                    // close_app();
                }
            }
        }
        Log.d(Constants.TAG, "foreground value:" + foreground);
        if (foreground) {
            foreground = false;
            close_app();
            Log.i(Constants.TAG, "Close App and start Activity:");

        } else {
            //if not foreground
            close_app();
            foreground = false;
            Log.i(Constants.TAG, "Close App");

        }

        return null;
    }
}

このようにAsyncTaskを実行します。 new CheckIfForeground().execute();


これへのリンクはありますか?
user1743524 2016

0

私は1つの方法で2つのソリューションを組み合わせましたが、API 24とAPI 21で機能します。その他のテストは行いませんでした。

Kotlinのコード:

private fun isAppInForeground(context: Context): Boolean {
    val appProcessInfo = ActivityManager.RunningAppProcessInfo()
    ActivityManager.getMyMemoryState(appProcessInfo)
    if (appProcessInfo.importance == IMPORTANCE_FOREGROUND ||
            appProcessInfo.importance == IMPORTANCE_VISIBLE) {
        return true
    } else if (appProcessInfo.importance == IMPORTANCE_TOP_SLEEPING ||
            appProcessInfo.importance == IMPORTANCE_BACKGROUND) {
        return false
    }

    val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    val foregroundTaskInfo = am.getRunningTasks(1)[0]
    val foregroundTaskPackageName = foregroundTaskInfo.topActivity.packageName
    return foregroundTaskPackageName.toLowerCase() == context.packageName.toLowerCase()
}

そしてマニフェストで

<!-- Check whether app in background or foreground -->
<uses-permission android:name="android.permission.GET_TASKS" />
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.