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

Posted

技术标签:

【中文标题】Android 5.1.1 及更高版本 - getRunningAppProcesses() 仅返回我的应用程序包【英文标题】:Android 5.1.1 and above - getRunningAppProcesses() returns my application package only 【发布时间】:2015-08-17 14:51:28 【问题描述】:

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

Lollipop 更新后,杀死 getRunningTasks(int maxNum) 并感谢 this answer,我使用此代码获取自 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

我们可以将新的UsageStatsManager API 用作described here,但它不适用于所有应用程序。一些系统应用会返回相同的包

com.google.android.googlequicksearchbox

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

一些应用程序使用AccessibilityService(如seen here),但它有一些缺点。


还有其他方法可以获取当前运行的应用包吗?

【问题讨论】:

Du Speed Booster 似乎有效。我不确定它是否使用 UsageStatsManager。 请注意,几家主要供应商已删除允许从其设备访问 UsageStats API 的系统活动。这意味着这些设备上的应用永远无法获得必要的权限。 Samsung 就是其中之一。这占市场的 60% 以上。 这是来自 android 错误跟踪器的关于此问题的票:code.google.com/p/android-developer-preview/issues/… 如果我解析在 shell 中运行 ps 的输出并且策略是 "fg" 并且 /proc/[pid]/oom_adj_score 的内容等于 0 那么应用程序就是前台应用程序。不幸的是,/proc/[pid]/oom_adj_score 在 Android 6.0 上似乎不再可读。 gist.github.com/jaredrummler/7d1498485e584c8a120e 【参考方案1】:

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

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

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

【讨论】:

@androiddeveloper 不,我只使用了 libsuperuser,因为它提供了一种简单的方法来运行 shell 命令(非 root 或 root)并获取输出。如果有足够的需求,我可以在没有 libsuperuser 的情况下重写它。 对不起。只是好奇而已。 :( @JaredRummler 我目前正在将您的解决方案实施到我的应用程序中。据我所知,似乎工作正常 @androiddeveloper,如果要根据不同的属性对其进行排序,请使Process 实现Comparable 并覆盖compareTo 方法。然后,当您使用Collections.sort(processesList) 时,它将使用您指定的顺序。 @BruceWayne 我认为 Jared 指的是这个链接:https://source.android.com/devices/tech/security/selinux/index.html【参考方案2】:
 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 link

【讨论】:

正如 OP 和 cmets 中所指出的,使用 UsageStatsManager 有很大的缺点。三星删除了请求,用户授予系统权限非常烦人。 我已经在 moto e2 设备上进行了测试。它工作正常,是的,我们需要实现此权限。我们都面临同样的问题。我们都真的需要更好的方法。祝你好运 这种方法至少在 LG G3 上不起作用,因为没有“设置 -> 安全 -> 具有使用访问权限的应用程序”菜单项 这不是我的问题的答案。此外,此答案中没有提供任何新信息。 在棉花糖中工作正常但不正常。如果收到通知,则当前运行的进程将是收到的包通知。实际上应用程序没有在前台或后台运行:(。需要解决方案。【参考方案3】:

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

【讨论】:

谢谢,但这里没有什么新鲜事。他只是包装了 OP 中提到的 UsageStatsManager API。用户需要启用访问权限,与 Android 5.1.1 之后的其他方法一样 @Jérémy 您是否请求了所需的权限?【参考方案4】:

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

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

【讨论】:

我看到 applock 还在 5.1.1 上工作,一旦应用在 security->"apps with usage access" 中获得了权限......这有点令人惊讶 "具有保护级别签名、特权或签名或系统的权限仅授予系统应用程序。如果应用程序是常规的非系统应用程序,它将永远无法使用这些权限。"说 android工作室.. 祝你好运与谷歌谈判被接受为“系统应用程序”。【参考方案5】:

只是对我想象的用于检测 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)。

【讨论】:

【参考方案6】:
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" />

然后在您的设备上启动服务和应用程序可访问性 设置->辅助功能->应用 在那个服务上。

【讨论】:

这已经包含在问题中,并且您刚刚从 *** 上的原始源代码复制粘贴了代码。【参考方案7】:

请尝试使用getRunningServices() 而不是getRunningAppProcesses() 方法。

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

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

【讨论】:

欢迎来到 Stack Overflow!我认为这不会起作用,因为前台应用程序可能没有运行服务。

以上是关于Android 5.1.1 及更高版本 - getRunningAppProcesses() 仅返回我的应用程序包的主要内容,如果未能解决你的问题,请参考以下文章

相机无法在纵向 android 2.2 及更高版本中工作

Android Api(Okhttps)未在android 9(pie)及更高版本中调用[重复]

pulltorefresh android 4.1及更高版本[重复]

Android 6 及更高版本中的 Android SSLSocket 握手失败

Android BLE write Characteristic 方法在 Android 5.0 及更高版本中总是返回 false

超链接不工作 Android OS 5 及更高版本