Android 5.1.1及更高版本-getRunningAppProcesses()仅返回我的应用程序包


100

看来Google终于关闭了获取当前前景应用程序包的所有门。

棒棒糖更新后,该消息被杀死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棉花糖),似乎已被杀死 getRunningAppProcesses()了。现在,它将返回您自己的应用程序包的列表。


UsageStatsManager

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

com.google.android.googlequicksearchbox

无障碍服务(2017年12月:将被Google禁止使用)

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


有没有其他方法来获取当前正在运行的应用程序包?


1
Du Speed Booster似乎有效。我不确定它是否使用UsageStatsManager。
thecr0w

3
请注意,一些主要的供应商已删除了允许从其设备访问UsageStats API的系统活动。这意味着这些设备上的应用将永远无法获得必要的许可。
凯文·克鲁姆维德

3
三星就是其中之一。那是超过60%的市场。
凯文·克鲁姆维德

3
这是来自android bug跟踪器的关于此问题的票证:code.google.com/p/android-developer-preview/issues/…–
Florian Barth

2
如果我解析ps在shell 中运行的输出,并且策略为"fg"和内容/proc/[pid]/oom_adj_score等于,0则该应用为前台应用。不幸的是,它似乎/proc/[pid]/oom_adj_score在Android 6.0上不再可读。gist.github.com/jaredrummler/7d1498485e584c8a120e
Jared Rummler

Answers:


93

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

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

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


4
@androiddeveloper不,我仅使用libsuperuser,因为它提供了一种运行shell命令(非root用户或root用户)并获取输出的简便方法。如果有足够的需求,我可以不用libsuperuser来重写它。
Jared Rummler,2015年

1
抱歉 只是好奇而已。:(
Android开发人员

1
@JaredRummler我目前正在将您的解决方案实施到我的应用程序中。据我所知,似乎工作正常
guy.gc 2015年

1
@androiddeveloper,如果要基于其他属性对其进行排序,请Process实现Comparable并重写该compareTo方法。然后,当您使用时Collections.sort(processesList),它将使用您指定的顺序。
斯里尼

2
@BruceWayne我认为Jared指的是此链接:https
Eduardo Herzer,2015年

24
 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的Clean matser Google Play链接


正如OP和评论中所指出的那样,使用的主要缺点UsageStatsManager。三星已删除了请求,并且用户授予系统权限非常烦人。
Jared Rummler

我已经在moto e2设备中进行了测试。一切正常,是的,我们需要实施此权限。我们都面临着同样的问题。我们所有人都确实需要更好的方法。祝你好运伙计
Tarun Sharma

2
此方法至少在LG G3上不起作用,因为没有“设置->安全->具有使用权限的应用”菜单项
Dmitry

2
那不是我的问题的答案。此外,此答案中没有提供新信息。
Lior Iluz 2015年

1
在棉花糖中可以正常工作,但不能正常工作。如果收到通知,则当前正在运行的进程将是收到的软件包通知。实际上应用程序是不是在前台或后台:(需要的解决方案运行。
WonderSoftwares

6

看看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);

2
谢谢,但这里没有新内容。他只是包装了OP中提到的UsageStatsManager API。用户将需要启用访问,由于Android 5.1.1其他方法
利奥尔Iluz

@Jérémy是否已请求所需的权限?
rvalerio

似乎它也在一直轮询,对不对?
Android开发人员

4

Google仅将此功能限制为系统应用程序。如错误票证中所述,您将需要REAL_GET_TASKS权限才能访问那里。

应用程序现在必须具有... permission.REAL_GET_TASKS才能获取所有应用程序的过程信息。如果没有许可,则仅返回调用应用程序的过程信息。如果特权应用程序没有新权限,但已弃用...权限,则它们将暂时能够获取所有应用程序的进程信息。


我看到applock仍在5.1.1上工作,一旦该应用在安全性->“具有使用权限的应用”中获得许可...这有点令人惊讶
mahesh ren

“具有保护级别签名,特权或signatureOrSystem的权限仅授予系统应用程序。如果某个应用程序是常规的非系统应用程序,则它将永远无法使用这些权限。” android studio说道。 Google被接受为“系统应用程序”。
杰里米

2

只是对我想象的可能是优化的代码进行了大量潜在的优化,以检测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秒的循环中使用了此代码,并且想知道为什么当Collections.max()中有一个更简单的解决方案O(n)时,为什么要使用一个复杂的解决方案O(n * log(n))。 )。


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

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


问题已经涵盖了这一点,您只需将代码从其原始源复制粘贴到StackOverflow上即可。
山姆

-2

请尝试使用getRunningServices()代替getRunningAppProcesses()方法。

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

 List<ActivityManager.RunningServiceInfo> appProcessInfoList = mActivityManager.getRunningServices(Integer.MAX_VALUE);

3
欢迎使用Stack Overflow!我认为这不会奏效,因为前台应用程序可能未运行服务。
2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.