103

谷歌似乎终于关闭了所有获取当前前台应用程序包的大门。

在 Lollipop 更新后,getRunningTasks(int maxNum)由于这个答案,我使用这个代码来获取前台应用程序包,因为 Lollipop:

final int PROCESS_STATE_TOP = 2;
RunningAppProcessInfo currentInfo = null;
Field field = null;
try {
    field = RunningAppProcessInfo.class.getDeclaredField("processState");
} catch (Exception ignored) { 
}
ActivityManager am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> appList = am.getRunningAppProcesses();
for (RunningAppProcessInfo app : appList) {
    if (app.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
        app.importanceReasonCode == 0 ) {
        Integer state = null;
        try {
            state = field.getInt( app );
        } catch (Exception ignored) {
        }
        if (state != null && state == PROCESS_STATE_TOP) {
            currentInfo = app;
            break;
        }
    }
}
return currentInfo;

Android 5.1.1 及更高版本(6.0 Marshmallow)似乎也被杀死getRunningAppProcesses()了。它现在返回您自己的应用程序包的列表。


UsageStatsManager

我们可以使用此处描述的新UsageStatsManagerAPI,但它不适用于所有应用程序。某些系统应用程序会返回相同的包

com.google.android.googlequicksearchbox

AccessibilityService(2017 年 12 月:将被 Google 禁止使用)

一些应用程序使用AccessibilityService(如此处所示),但它有一些缺点。


是否有另一种获取当前正在运行的应用程序包的方法?

4

7 回答 7

95

要获取 Android 1.6 - Android 6.0 上正在运行的进程列表,您可以使用我写的这个库:https ://github.com/jaredrummler/AndroidProcesses该库读取 /proc 以获取进程信息。

Google 在 Android Nougat 中显着限制了对 /proc 的访问。要获取 Android Nougat 上正在运行的进程列表,您需要使用 UsageStatsManager 或具有 root 访问权限。

单击以前的替代解决方案的编辑历史记录。

于 2015-09-03T03:27:39.817 回答
29
 private String printForegroundTask() {
    String currentApp = "NULL";
    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        UsageStatsManager usm = (UsageStatsManager)this.getSystemService("usagestats");
        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("adapter", "Current App in foreground is: " + currentApp);
    return currentApp;
}

使用此方法获取前台任务。你需要一个系统权限“android:get_usage_stats”

public static boolean needPermissionForBlocking(Context context){
    try {
        PackageManager packageManager = context.getPackageManager();
        ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);
        AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
        return  (mode != AppOpsManager.MODE_ALLOWED);
    } catch (PackageManager.NameNotFoundException e) {
        return true;
    }
}

如果用户在设置 -> 安全 -> 具有使用访问权限的应用程序中启用此功能。之后你会得到前台任务。Cheetahamobile google play 链接的类似过程 Clean matser

于 2015-09-01T11:28:07.190 回答
7

看看https://github.com/ricvalerio/foregroundappchecker,它可能是你需要的。提供示例代码,消除了必须实现跨版本前台检测器的痛苦。

以下是两个示例:

AppChecker appChecker = new AppChecker();
String packageName = appChecker.getForegroundApp();

或定期检查:

AppChecker appChecker = new AppChecker();
appChecker
    .when("com.other.app", new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .when("com.my.app", new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .other(new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .timeout(1000)
    .start(this);
于 2016-08-28T23:06:30.737 回答
4

Google 将此功能仅限于系统应用程序。正如bug 票证中所报告的,您需要REAL_GET_TASKS权限才能访问那里。

应用程序现在必须具有 ...permission.REAL_GET_TASKS 才能获取所有应用程序的进程信息。如果应用没有权限,只会返回调用应用的进程信息。如果权限应用程序没有新权限但已弃用 ...permission.GET_TASKS ,则权限应用程序将暂时能够获取所有应用程序的进程信息。此外,只有系统应用程序可以获得 REAL_GET_TASKS 权限。

于 2015-09-02T15:09:01.073 回答
3

只是对我想象的一个潜在的优化进行了优化,它是大量复制粘贴的代码,用于检测 Android M 上最顶层的应用程序。

这个

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    UsageStatsManager usm = (UsageStatsManager)this.getSystemService("usagestats");
    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();
        }
    }
}

可以简化为

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    UsageStatsManager usm = (UsageStatsManager) context.getSystemService(
        Context.USAGE_STATS_SERVICE);
    long time = System.currentTimeMillis();
    List<UsageStats> appStatsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
            time - 1000 * 1000, time);
    if (appStatsList != null && !appStatsList.isEmpty()) {
        currentApp = Collections.max(appStatsList, (o1, o2) ->
            Long.compare(o1.getLastTimeUsed(), o2.getLastTimeUsed())).getPackageName();
    }
}

我发现自己在 2 秒的循环中使用了这段代码,并且想知道为什么我使用复杂的解决方案是 O(n*log(n)),而 Collections.max() 中提供了一个更简单的解决方案,即 O(n )。

于 2017-12-01T01:07:31.493 回答
0
public class AccessibilityDetectingService extends AccessibilityService {

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

    //Configure these here for compatibility with API 13 and below.

    AccessibilityServiceInfo config = new AccessibilityServiceInfo();
    config.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
    config.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;

    if (Build.VERSION.SDK_INT >= 16)
        //Just in case this helps
        config.flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;

    setServiceInfo(config);
}

@Override
public void onAccessibilityEvent(final AccessibilityEvent event) {
        if (event == null ) {
            return;
        } else if(event.getPackageName() == null && event.getClassName() == null){
            return;
        }

            if (activityInfo != null){

                Log.d("CurrentActivity", componentName.flattenToShortString());
        }

}

private ActivityInfo tryGetActivity(ComponentName componentName) {
    try {
        return getPackageManager().getActivityInfo(componentName, 0);
    } catch (PackageManager.NameNotFoundException e) {
        return null;
    }
}
@Override
public void onInterrupt() {
}                
}
}//`enter code here`uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
<uses-permission android:name="android.permission.GET_TASKS" />

然后在您的设备设置->可访问性->该服务上的应用程序中启动服务和应用程序可访问性。

于 2016-02-02T11:22:57.813 回答
-3

Please try to use getRunningServices() instead of getRunningAppProcesses() method.

 ActivityManager mActivityManager = (ActivityManager) getSy stemService(Context.ACTIVITY_SERVICE);

 List<ActivityManager.RunningServiceInfo> appProcessInfoList = mActivityManager.getRunningServices(Integer.MAX_VALUE);
于 2015-10-09T09:23:11.263 回答