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

在Lollipop更新之后,它杀死了getRunningTasks(int maxNum),并感谢此回答,自Lollipop起,我使用以下代码来获取前台应用程序包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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,但它不适用于所有应用程序。 某些系统应用程序将返回相同的程序包

1
com.google.android.googlequicksearchbox

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

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

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

  • 关于UsageStatsManager的问题,您是否对Android L中的辅助功能服务或getRunningAppProcesses有相同的问题?
  • @山姆我在Android M上的应用程序中测试了AccessibilityService,即使对于那些系统应用程序,UsageStats似乎也没有得到正确的软件包。但是,使用"可访问性"方式时出现一个奇怪的计时问题,在它已经报告了正确的程序包之后,有时有时会报告两次不同的程序包。
  • Du Speed Booster似乎有效。我不确定它是否使用UsageStatsManager。
  • 请注意,一些主要的供应商已删除了允许从其设备访问UsageStats API的系统活动。这意味着这些设备上的应用将永远无法获得必要的许可。
  • 三星就是其中之一。那就是超过60%的市场。
  • 这是来自android bug跟踪器的关于此问题的票证:code.google.com/p/android-developer-preview/issues/
  • 如果我在外壳中解析运行ps的输出,并且策略为"fg",并且proc[pid]oom_adj_score的内容等于0,则该应用程序为前台应用程序。不幸的是,似乎proc[pid]oom_adj_score在Android 6.0上不再可读。 gist.github.com/jaredrummler/7d1498485e584c8a120e
  • 有人可以运行我刚才在下面的答案中输入的脚本吗?谢谢。
  • @JaredRummler我在Android 5.1上测试了您的代码。我只需要作一些调整,就像我在您的答案中评论的那样。您是否已确认它是否可以在Android 6.0上运行?
  • 您可以添加更多信息吗?也许足以确定您自己的应用程序是否在前台,这可以通过在活动中使用静态变量轻松完成。
  • @bjornson查找我自己的应用程序是否在前台/后台,您可以使用ActivityLifecycleCallbacks。不需要静态变量。我需要它来了解哪个应用程序正在前台运行,如问题中所述。
  • 好的,谢谢您,但是您可能对Android的新安全性概念有疑问。虽然还不太能获得ActivityLifecycleCallbacks。如何使用ActivityLifecycleCallbacks在Service类中检查我的主要活动是否在前台?我在可见活动中使用了静态变量,因此在侦听Google Cloud Messaging的服务中,我可以决定是否需要通知或只是告诉视图进行刷新。
  • @bjornson我不太确定我是否了解您的特定要求,但是有很多关于如何在StackOverflow上使用ActivityLifecycleCallbacks的示例。
  • @LiorIluz能否列出使用android提供的辅助功能的缺点?
  • @Neji的缺点在这里得到了很好的描述stackoverflow.com/questions/3873659/也链接到我的问题中。尽管存在缺点,我还是选择使用它,主要是因为目前没有更好的选择。
  • 牛轧糖设备有什么解决方法吗?
  • Android的可能副本:如何获取当前的前台活动(从服务)?
  • 我投票决定将其作为重复项关闭,因为在StackOverflow上有太多有关此问题的问题,原始问题很好地涵盖了大多数技术。

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

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

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

  • 使用" libsuperuser"是否意味着您需要root用户才能在Android 6上获取最新/正在运行的应用程序?
  • @androiddeveloper不,我仅使用libsuperuser,因为它提供了一种运行shell命令(非root用户或root用户)并获取输出的简便方法。如果有足够的需求,我可以不用libsuperuser来重写它。
  • 我知道了。流程列表是否以某种方式排序?它是当前正在运行的应用程序的列表,对吧?另外,使用此命令和使用" top"有什么区别?两者的输出大致相同,不是吗?
  • @androiddeveloper由pid排序。它与top基本上相同。我使用ps是因为我知道它已经与systembintoolbox分开了一段时间了。我相信pstop都从proc[pid]stat获取信息。
  • 我实际上不了解"工具箱"命令。是不是"顶部"更常用?关于按pid排序,是否有办法使它按其他方式排序?
  • @androiddeveloper我认为我们在这里浪费评论空间。看看topps的来源。 github.com/android/platform_system_core/tree/master/toolbox如果您想继续任何讨论,我的电子邮件在我的个人资料中。基本上,ps快一点。
  • 抱歉只是好奇而已。 :(
  • @JaredRummler Im目前正在将您的解决方案实施到我的应用程序中。据我所知似乎工作正常
  • 如果您正在寻找特定的程序包名称,则可能需要通过更改return语句来修改getForeGroundApp,以减少空白:return frontProcess.trim();否则似乎没有尾随空白。所有使用oom值的方法都很棒! @JaredRummler
  • 效果很好,但是效果太好了。继续随机分配其他进程,必须花点时间检查您要查看的应用程序是否是前台进程。
  • @androiddeveloper,如果要基于其他属性对其进行排序,请使Process实现Comparable并覆盖compareTo方法。然后,当您使用Collections.sort(processesList)时,它将使用您指定的顺序。
  • @BruceWayne首先,您不需要这样做,因为您具有带有Comparator参数的排序功能(此处的示例:developer.android.com/reference/java/util/Arrays.html#sort(T[],java。 util.Comparator <?super T>))。其次,我想按发布时间排序,在那找不到。
  • @androiddeveloper,如果您不相信Process的自然顺序是其名称的词法顺序,也可以使用,就像您说的那样使用比较器。
  • @BruceWayne这取决于您希望如何排序。
  • @JaredRummler,您能否详细说明一下SELinux是强制执行的手段?
  • 有没有不使用libsuperuser的解决方案?如果有,我将不胜感激。
  • 在Moto G上进行测试:我必须将if (lines.length != 2)更改为if (lines.length != 3),将String cpuaccctSubsystem = lines[1];更改为String cpuaccctSubsystem = lines[2];。另外,我在return语句中添加了trim(),因此它消除了一些垃圾。其他人是否在其他设备中测试了没有lib的hack代码?
  • @BruceWayne我认为Jared指的是此链接:https://source.android.com/devices/tech/security/selinux/index.html
  • 有什么方法可以防止在遍历文件时显示此消息? avc: denied { getattr } for path="proc18" dev="proc" ino=10667 scontext=u:r:untrusted_app:s0:c512,c768 tcontext=u:r:kernel:s0 tclass=dir permissive=0
  • 这是一个非常有趣的解决方法。但是,当运行命令toolbox ps时,返回的第一行(到stdout)将是标头,应该将其丢弃,否则将导致异常,记录错误消息Failed parsing line ...一个简单的stdout.remove(0)设法为我修复它,但由于怀疑它在不同设备上可能有所不同,所以我没有直接编辑您的答案?
  • 我在华为P8(Andriod版本5.0.1)中测试了getForegroundApp()。还需要像@Eduardo Herzer一样将if (lines.length != 2) to if (lines.length != 3)String cpuaccctSubsystem = lines[1];更改为String cpuaccctSubsystem = lines[2];。在大多数情况下,它起作用,除了启动拨号程序或联系人时返回android.process.acore而不是实际的程序包名称。
  • github.com/android/platform_system_core/commit/
  • @JaredRummler Enable hidepid=2 on proc是什么意思?它的低级更改,不是Java语言,对不对?
  • 我可以看到正在运行的应用程序列表。但是,如何确定哪个应用程序是最重要的呢?
  • 前景应用程序检测确认可在Asus Zenfone(5.1),Samsung Galaxy(5.0)上运行。华硕Zenfone的一个问题是,列表中的第一项始终是com.mediatek.nlpservice,列表中的第二项是正确的答案:前台应用程序。问题:您是否希望在后续版本的android中解决此漏洞?即使执行SE linux时,前台应用程序检测(不是整个进程列表)是否正确?
  • 仅测试前景ap检测的更新。确认正在使用Samsung Note 4,Asus Zenfone和Samsung Galaxy S4。不适用于HTC One 8,Sony Experia。其他可以进行测试的人可以发表自己的经历吗?
  • @JaredRummler对此表示感谢。不幸的是,使用N预览进行了测试,ProcessManager.getRunningForegroundApps(context)返回一个空字符串,而ProcessManager.getRunningAppProcesses()仅返回我的应用名称。
  • @Favolas Im意识到它不适用于N开发人员预览版1。我现在没有时间研究它,我很可能等到发布预览版4或最终版。
  • 谢谢您,先生,这个答案:)
  • getForegroundApp()始终返回com.nuance.xt9.input(键盘应用程序?),因为它始终具有oomscore值1,该值低于所有其他值。 Nexus 7(2013),Android 6.0.1上的Testes
  • 在具有Android 4.4.2的Samsung Tab Pro 8.4上,由于cpuaccctSubsystem永远不会以pid结尾,所以frontenProcess始终为null。为什么?在Nexus 5 / M上也遇到过这样的情况,尽管它们在前台,但Skype的oomscore最终比启动器高
  • 我已经在各种手机上对其进行了测试,我可以说它在api 23之前都可以正常工作。在具有Android 7的nexus 5X上,前台应用程序始终为null。另外,有没有办法检测Facebook聊天头之类的服务(覆盖)?
  • 很棒的图书馆,保存了我的一天。
  • 也许您现在不记得了,但是如果还有其他人这样做,那么Nougat设备是否有任何解决方法?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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"

1
2
3
4
5
6
7
8
9
10
11
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的清洁垫
谷歌播放链接

  • 正如OP和注释中所指出的那样,使用UsageStatsManager存在主要缺点。三星已经删除了请求,并且用户授予系统权限非常烦人。
  • 我已经在moto e2设备中进行了测试。一切正常,是的,我们需要实施此权限。我们都面临着同样的问题。我们所有人确实需要更好的方法。祝你好运伙计
  • 此方法至少在LG G3上不起作用,因为没有"设置->安全->具有使用权限的应用"菜单项
  • 那不是我的问题的答案。此外,此答案中没有提供新信息。
  • 如何在这里杀死所有服务@Tarun Sharma
  • 在棉花糖中可以正常工作,但不能正常工作。如果收到通知,则当前正在运行的进程将是收到的软件包通知。实际应用未在前台或后台运行:(。需要解决方案。
  • UsageStatsManager返回Samsung S6 / S7上的填充列表。
  • 您的解决方案在Pixel XL上不返回任何内容。
  • @ Clocker&@Igor ..我们只能使用UsageStatsManager来找到一些解决方案。棉花糖及更高版本中,不建议运行进程
  • 如果要跟踪用户进度,请获取可访问性权限。但请注意,可访问性权限是敏感权限

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

这是两个示例:

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

或定期检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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以来的其他方法一样
  • @Jrmy是否请求了所需的权限?

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

Applications must now have ...permission.REAL_GET_TASKS to be able to
get process information for all applications. Only the process
information for the calling application will be returned if the app
doesn't have the permission. Privileges apps will temporarily be able
to get process information for all applications if they don't have the
new permission, but have deprecated ...permission.GET_TASKS Also,only
system apps can acquire the REAL_GET_TASKS permission.

  • 我看到applock仍在5.1.1上工作,一旦该应用在安全性->"具有使用权限的应用"中获得许可...这有点令人惊讶
  • "具有保护级别签名,特权或signatureOrSystem的权限仅授予系统应用程序。如果某个应用程序是常规的非系统应用程序,则它将永远无法使用这些权限。" android studio说道。 Google被接受为"系统应用程序"。

只是对我想象的可能是优化的代码进行了大量潜在的优化,以检测Android M上最顶级的应用程序。

这个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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();
        }
    }
}

可以简化为

1
2
3
4
5
6
7
8
9
10
11
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))。 )。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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上即可。

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

1
2
3
 ActivityManager mActivityManager = (ActivityManager) getSy stemService(Context.ACTIVITY_SERVICE);

 List<ActivityManager.RunningServiceInfo> appProcessInfoList = mActivityManager.getRunningServices(Integer.MAX_VALUE);
  • 欢迎使用Stack Overflow!我认为这不会起作用,因为前台应用程序可能未运行服务。