Android 获取前台应用的方法总结

本文总结了在不同Android版本下获取前台应用的方法,包括Android 5.0以前使用getRunningTasks(),Android 5.0使用getRunningAppProcesses(),以及Android 5.1及之后版本采用AccessibilityService和UsageStatsManager。对于5.1及以上版本,推荐使用UsageStatsManager,因为getRunningAppProcesses()受限,而AccessibilityService虽然稳定但需要辅助权限。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    应用开发中,经常遇到需要获取系统正在运行的前台应用是什么应用,例如桌面悬浮窗挂件,需要获取当前是不是处于桌面;然而随着Android版本的迭代,获取前台应用的方法都陆续被制裁,因此对这些方法做一个总结。

Android 5.0以前版本 getRunningTasks()


在Android 5.0以前的版本中,google提供了getRunningTasks()方法来获取所有正在运行的应用程序,获取前台应用可以通过下面的代码实现

  public static String getTopAppPackageName(Context context) {
        String packageName = "";
        try {
            if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){
                ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
                List<ActivityManager.RunningTaskInfo> rti = mActivityManager.getRunningTasks(1);
                packageName = rti.get(0).topActivity.getPackageName();
            }
        }catch (Exception ignored){
        }
        return packageName;
    }

其中Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP判断 是否是5.0之前的版本,因为在Android 5.0版本开始,getRunningTasks()方法只能返回本应用和launcher。

Android 5.0 getRunningAppProcesses()


在Android 5.0中,google新增了getRunningAppProcesses()方法,用来获取所有当前运行的应用,具体代码实现如下


    public static String getTopAppPackageName(Context context) {

        String packageName = "";
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        try {
            if(Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
                List<ActivityManager.RunningAppProcessInfo> processes = activityManager.getRunningAppProcesses();
                if (processes.size() == 0) {
                    return packageName;
                }
                for (ActivityManager.RunningAppProcessInfo process : processes) {

                    if (process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                        return process.processName;
                    }
                }
            }
        }catch (Exception ignored){
        }
        return packageName;
    }

其中Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP表示为Android 5.0时,通过getRunningAppProcesses()方法来获取当前所有运行的应用,然后通过importance来判断是否处于前台

IMPORTANCE_FOREGROUND:值为100,表示应用处于前台,且未锁屏
IMPORTANCE_TOP_SLEEPING:值为150,表示应用处于前台,且手机锁屏
IMPORTANCE_SERVICE:值为300,表示应用处于后台

更多可以查看ActivityManager.RunningAppProcessInfo中的定义


Android 5.1及之后版本


Android 5.1及之后版本,getRunningAppProcesses()方法也被制裁了,也只能获取本应用的信息,只能通过其他方式来解决。下面列举一些解决方法

AccessibilityService

通过AccessibilityService辅助权限来解决吗,但是辅助权限能干很多“流氓”的事,不推荐使用。

优点

  • AccessibilityService 有非常广泛的 ROM 覆盖,特别是非国产手机,从 Android API Level 18(Android 2.2) 到 Android Api Level 23(Android 6.0)

  • AccessibilityService 不再需要轮询的判断当前的应用是不是在前台,系统会在窗口状态发生变化的时候主动回调,耗时和资源消耗都极小

  • 不需要再额外申请权限

  • 方法比较稳定,并非利用 Android 一些设计上的漏洞,可以长期使用的可能很大

  • 可以用来判断任意应用甚至 Activity, PopupWindow, Dialog 对象是否处于前台

缺点

  • 需要要用户开启辅助功能,辅助权限能干很多“流氓”的事,并不推荐

  • 辅助功能会伴随应用被“强行停止”而剥夺

UsageStatsManager

    Android 5.0,google 新增了UsageStatsManager类,但需要添加PACKAGE_USAGE_STATS权限,属于系统权限,需要引导用户开启。实现方式如下

1.在AndroidManifest文件中添加权限

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

2.启动授权页面,引导用户授权

Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);  
context.startActivity(intent); 

3.当用户授权完成之后,通过代码获取UsageEvents Event,获取最近的一个Event,就是当前前台的应用

 public static String getTopAppPackageName(Context context) {

        String packageName = "";
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

      if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP){
                final long end = System.currentTimeMillis();
                final UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService( Context.USAGE_STATS_SERVICE);
                if (null == usageStatsManager) {
                    return packageName;
                }
                final UsageEvents events = usageStatsManager.queryEvents((end - 60 * 1000), end);
                if (null == events) {
                    return packageName;
                }
                UsageEvents.Event usageEvent = new UsageEvents.Event();
                UsageEvents.Event lastMoveToFGEvent = null;
                while (events.hasNextEvent()) {
                    events.getNextEvent(usageEvent);
                    if (usageEvent.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
                        lastMoveToFGEvent = usageEvent;
                    }
                }
                if (lastMoveToFGEvent != null) {
                    packageName = lastMoveToFGEvent.getPackageName();
                }
            }
        }catch (Exception ignored){
        }
        return packageName;
    }


使用ps命令,获取到运行进程的pid

Github上有一个国外的开源项目,通过ps命令实现的,但是貌似Android 6.0也不能使用了,我在7.0上试了,确实不行,有兴趣的可以去试试

地址:https://github.com/Chainfire/libsuperuser

总结


在Android 5.1及之后版本,推荐使用UsageStatsManager来获取前台应用,其次是AccessibilityService,下面贴出通过UsageStatsManager实现的方法代码

public static String getTopAppPackageName(Context context) {
        String packageName = "";
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        try {
            if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){
                List<ActivityManager.RunningTaskInfo> rti = activityManager.getRunningTasks(1);
                packageName = rti.get(0).topActivity.getPackageName();
            } else if(Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
                List<ActivityManager.RunningAppProcessInfo> processes = activityManager.getRunningAppProcesses();
                if (processes.size() == 0) {
                    return packageName;
                }
                for (ActivityManager.RunningAppProcessInfo process : processes) {
                    if (process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                        return process.processName;
                    }
                }
            } else if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP){
                final long end = System.currentTimeMillis();
                final UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService( Context.USAGE_STATS_SERVICE);
                if (null == usageStatsManager) {
                    return packageName;
                }
                final UsageEvents events = usageStatsManager.queryEvents((end - 60 * 1000), end);
                if (null == events) {
                    return packageName;
                }
                UsageEvents.Event usageEvent = new UsageEvents.Event();
                UsageEvents.Event lastMoveToFGEvent = null;
                while (events.hasNextEvent()) {
                    events.getNextEvent(usageEvent);
                    if (usageEvent.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
                        lastMoveToFGEvent = usageEvent;
                    }
                }
                if (lastMoveToFGEvent != null) {
                    packageName = lastMoveToFGEvent.getPackageName();
                }
            }
        }catch (Exception ignored){
        }
        return packageName;
    }
<think>好的,我现在需要回答用户的问题:如何在Android获取前台应用的UID。首先,我得回忆一下Android中关于应用管理和进程调度的相关知识。用户提到的UID是指用户ID,每个应用在安装时都会被分配一个唯一的UID,用于权限管理和进程隔离。 根据提供的引用内容,尤其是引用[3]提到了确定prev的可见性,其中涉及ActivityTaskManager确保活动可见性。这可能与当前前台活动有关,因为系统服务需要跟踪哪个Activity处于前台。因此,获取前台应用的UID可能需要通过ActivityManager或ActivityTaskManager来获取当前运行的任务或活动,进而得到对应的应用信息,包括UID。 接下来,我需要考虑具体的实现方法。在Android中,通常可以通过ActivityManager来获取正在运行的应用进程信息。例如,使用getRunningAppProcesses()方法,但这个方法可能在不同版本上有不同的限制,尤其是在Android 8.0(API 26)之后,可能无法获取到其他应用的信息,除非有特定权限。 另一种方法是使用UsageStatsManager,通过查询最近的使用情况来推断当前前台应用。但这种方法需要用户授予权限,并且可能不是实时的。 另外,引用[4]提到ActivityThread.main()方法应用进程启动时的作用,这里涉及到应用进程的创建和初始化。不过,这可能不直接帮助获取前台应用的UID,但说明系统服务如ActivityManagerService在管理应用进程中的作用。 引用[5]中提到的权限管理,特别是需要用户授权特殊权限,可能意味着在获取其他应用信息时需要相应的权限,比如PACKAGE_USAGE_STATS权限,或者使用系统级API,这可能需要系统签名权限,普通应用可能无法直接调用。 结合这些信息,可能的步骤是: 1. 使用ActivityManager获取最近的任务或运行中的进程。 2. 从这些任务中提取顶部的Activity信息,进而得到包名。 3. 通过包名获取对应的应用信息,包括UID。 需要注意的是,不同Android版本的限制和权限要求。例如,Android 5.0(API 21)之后,getRunningAppProcesses()可能不再返回其他应用的信息,除非应用具有特定权限。因此,可能需要其他方法,如使用UsageStatsManager或访问系统服务API。 此外,对于系统应用或具有特定权限的应用,可能可以直接调用系统级方法,如通过ActivityTaskManager的内部方法获取当前焦点Activity的信息,但这通常需要系统权限或反射调用,对普通应用来说不可行。 因此,一个可行的方法可能是: - 对于有权限的应用,使用ActivityManager的getRunningTasks()方法(但该方法Android 5.0后已被废弃,需要USE_FULL_SCREEN_INTENT权限)。 - 或者使用UsageStatsManager查询最近事件,找出当前前台应用。 - 获取到包名后,通过PackageManager的getApplicationInfo()得到UID。 但需要注意权限问题和API限制。例如,getRunningTasks()需要REQUEST_IGNORE_BATTERY_OPTIMIZATIONS权限,而UsageStatsManager需要PACKAGE_USAGE_STATS权限,这需要用户手动授权。 另外,引用[2]提到ActivityThread的attach方法,涉及系统进程间通信,普通应用可能无法直接访问这些内部方法,但系统应用可能有其他途径。 总结可能的实现步骤: 1. 检查并获取必要权限。 2. 使用ActivityManager或UsageStatsManager获取当前前台应用的包名。 3. 通过包名获取UID。 需要处理不同Android版本的适配问题,并确保权限正确获取,避免安全异常。</think>在Android获取前台应用的UID通常需要以下步骤: ### 方法实现 1. **获取当前前台应用的包名** 通过`ActivityManager`获取正在运行的任务列表,并提取顶部Activity的包名: ```java ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1); String packageName = tasks.get(0).topActivity.getPackageName(); ``` > **注意**:`getRunningTasks()`在Android 5.0(API 21)后需要`android.permission.GET_TASKS`权限[^4],且可能仅返回自身应用的信息。 2. **通过包名查询UID** 使用`PackageManager`根据包名获取应用信息,进而提取UID: ```java PackageManager pm = context.getPackageManager(); ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0); int uid = appInfo.uid; ``` ### 适配高版本系统的替代方案 对于Android 5.0及以上版本,可使用`UsageStatsManager`: ```java UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); long currentTime = System.currentTimeMillis(); List<UsageStats> stats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, currentTime - 1000*60, currentTime); if (stats != null) { UsageStats recentStats = null; for (UsageStats usageStats : stats) { if (recentStats == null || usageStats.getLastTimeUsed() > recentStats.getLastTimeUsed()) { recentStats = usageStats; } } String packageName = (recentStats != null) ? recentStats.getPackageName() : null; } ``` > 需要声明`android.permission.PACKAGE_USAGE_STATS`权限,并引导用户在设置中手动授权[^5]。 ### 系统级权限限制 - 若需直接通过系统API(如`ActivityTaskManager`内部方法获取,需系统签名权限,普通应用无法调用[^3]。 - 反射调用系统隐藏API可能导致兼容性问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值