Android14 通过AMS 实例获取前台Activity 信息

Android14 通过AMS 实例获取前台Activity 信息

文章目录

一、前言

本文主要讲一下:Android 通过AMS 实例获取Activity 信息的一些示例讲解

比如:获取当前存活的Activity界面;获取后台任务列表等。

一般应用场景是:清后台或者想跳转到最近任务界面;

本来不想写的,我以为这东西百度一下马上就出来了,感觉是很容易的东西;

豆包AI搜索出来的代码:

复制代码
// 获取ActivityManager实例(AMS的客户端代理)
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        
// 获取当前运行的任务信息,最多返回1个任务(当前前台任务)
List<ActivityManager.RunningTaskInfo> runningTasks = activityManager.getRunningTasks(1);

if (runningTasks != null && !runningTasks.isEmpty()) {
	// 获取当前任务中Activity的数量
	int activityCount = runningTasks.get(0).numActivities;
	Log.d(TAG, "当前任务中的Activity数量: " + activityCount);
} else {
	Log.d(TAG, "未获取到运行中的任务");
}

//getRunningTasks 已经废弃,需要使用getAppTasks方法
List<ActivityManager.AppTask> appTasks = activityManager.getAppTasks();
if (appTasks != null) {
    Log.d(TAG, "当前应用的任务数量: " + appTasks.size());
}

// 可选:获取当前运行的所有应用进程信息
List<ActivityManager.RunningAppProcessInfo> processes = activityManager.getRunningAppProcesses();
Log.d(TAG, "当前运行的应用进程数量: " + (processes != null ? processes.size() : 0));

RunningTaskInfo包含一些应用信息,看起来比较正确;

但是旁边同事说网上找到的答案和AI搜索到的都是验证不行。

应该是有坑的。所以这里总结记录一下。有需要的可以收藏查看。

本文的总结是有些网上目前没有的知识点,后续展示的其他内容也是有价值的。

二、AMS 实例获取前台Activity 信息

1、分析过程

运行了上面的AI搜索的代码,发现返回的只有当前demo应用的界面信息,无法获取其他后台的应用信息!

然后发现添加系统签名才能获取到后台其他的应用信息。

所以soga.

第一点需要注意的就是:应用需要系统签名才能获取后台应用信息。

下面所有的代码都是基于系统签名进行验证有用的代码。

2、获取存活的Activity应用

存活的Activity 指的是app界面未退出的应用,比如进入应用后按Home退出的应用;

如果是一直"返回"退出的应用,是不存在存活Activity的。

复制代码
// 获取ActivityManager实例(AMS的客户端代理)
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);

// 获取当前运行的任务信息(当前前台任务)
List<ActivityManager.RunningTaskInfo> runningTasks=activityManager.getRunningTasks(30);
Log.d(TAG, "当前任务中的Activity数量 runningTasks: " + runningTasks.size());
        for (ActivityManager.RunningTaskInfo runningTask : runningTasks) {
            Log.d(TAG, "前台任务ID: " + runningTask.id + runningTask.toString()); //runningTask.toString() 包含了大部分内容
            tv_info.append("\n前台任务ID: " + runningTask.id + ", topActivity = " + runningTask.topActivity);
            int activityCount = runningTask.numActivities;
            tv_info.append("当前任务中的Activity数量: " + activityCount);
        }

通过验证发现 getRunningTasks 还是可以使用,需要设置一个比较大的任务列表数值,就会返回多个任务对象;

最新的 getAppTasks() 方法反而没啥作用,只能返回本身对象;

3、获取最近任务

这里指的是最近的后台任务。

Android 界面上划显示的后台界面任务列表就是通过这种方式获取的。

复制代码
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RecentTaskInfo> recentTasks = am.getRecentTasks(Integer.MAX_VALUE, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
for (ActivityManager.RecentTaskInfo task : recentTasks) {
	Log.d(TAG, "Task ID: " + task.id);
	Log.d(TAG, "Top Activity: " + task.topActivity); //通过这个可以预览或者跳转到最近使用的应用界面
	// 其他相关信息...
}

上面获取到的最近任务列表 会存在部分task.topActivity 为null的情况,这些一般就会跳过显示。

清除后台应用一般就是循环forceStopPackage应用,之后在最近任务无法获取到它了。

有时候获取到的 存活的Activity应用列表和 最近任务列表是一样的。

普通Activity应用,后退back退出界面,虽然没有存活的Activity,但是它还是会显示在最近的后台应用列表中。

虽然应用的Activity不存在了,也没有后台服务,但是系统还是会有缓存保留,

缓存进程还在,下次直接打开Activity,还是之前的应用进程id,通过ps -ef | grep 包名 可以验证。

如果调用forceStopPackage应用或者强制kill应用进程,重新打开应用就会生成新的进程。

需要注意的是,缓存进程不会保存很久,系统会存在不定期清除的情况。

网上的说法是上面最近任务的获取方式已经过期,需要使用UsageStatsManager.queryUsageStats。

比如下面代码:

复制代码
        UsageStatsManager  usm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
        List<UsageStats> usageStats = usm.queryUsageStats(
                UsageStatsManager.INTERVAL_DAILY,
                System.currentTimeMillis() - TimeUnit.DAYS.toMillis(24), // 最近24小时
                System.currentTimeMillis()
        );
        Log.d(TAG, "24小时内启动过的进程数: " + usageStats.size());
        tv_info.append("\n 24小时内启动过的进程数: " + usageStats.size());
        for (UsageStats stats : usageStats) {
//            if ( stats.getLastTimeUsed() > 0) {
                Log.d(TAG, "Package Name: " + stats.getPackageName() + ", Last Time Used: " + stats.getLastTimeUsed() + ", Last Time Visible: " + stats.getLastTimeVisible());
//            }

        }

但是实际测试上面代码是不行的,stats.getLastTimeUsed() 方法返回的值基本都是零。

虽然返回的UsageStats列表数据比较多,但是UsageStats对象没啥具体数据,只有个不准确的时间戳和包名。

所以获取最近任务还是需要使用 ActivityManager.getRecentTasks ,

有些方法虽然过期,但是对于系统或者系统应用来说是稳定/正常使用的。

3、获取存活进程

后台的任务值的是存活的应用,有存活的Activity或者后台Service都是存活的应用。

复制代码
// 获取ActivityManager实例(AMS的客户端代理)
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);

// 可选:获取当前运行的所有应用进程信息,类似 ps -ef
List<ActivityManager.RunningAppProcessInfo> processes = activityManager.getRunningAppProcesses();

Log.d(TAG, "当前运行的应用进程数量: " + (processes != null ? processes.size() : 0));
tv_info.append("\n当前运行的应用进程数量: " + (processes != null ? processes.size() : 0));
for (ActivityManager.RunningAppProcessInfo process : processes) {
	Log.d(TAG, "进程名: " + process.processName + " ,importance = " + process.importance );
	tv_info.append("\n进程名: " + process.processName + " ,importance = " + process.importance + ",process.uid = " + process.uid + ",process.pid = " + process.pid);
}

上面这个获取RunningAppProcessInfo对象的场景可能是需要判断某个应用是否存活才需要。

对比一下后台任务和ps -ef

ps -ef 也是获取系统目前存活的进程,不仅仅是系统应用,还有些底层进程。

ps -ef 过滤某个应用的信息示例:

复制代码
用户类型      父进程ID(ppid) 进程ID(pid) 启动时间 程序计数器 应用包名
console:/ # ps -ef | grep com.android.settings                                 
system        1219   539 0 20:40:35 ?     00:00:16 com.android.settings
root          4081  5439 4 21:47:26 ttyS0 00:00:00 grep com.android.settings
//普通应用
u0_a36        9085   483 20 19:07:55 ?    00:00:06 org.skg.chromium

很多人以为 ps -ef第二三个数值是pid和uid,其实不是,是(父)进程id,有些应用会存在多个子进程。

RunningAppProcessInfo 的pid和ps -ef 中的ppid是一样的;

注意 ,RunningAppProcessInfo 的uid和ps -ef 中的pid是不一样的;

RunningAppProcessInfo 的uid 一般是指应用签名的用户id,如果是系统签名就是1000,否则就是其他。

三、其他

1、AMS 实例获取前台Activity 信息 小结

复制代码
(1)获取系统各个Activity应用的信息必须是系统签名,否则只能获取到自身应用信息;

(2)获取存活的Activity应用使用的api://返回 List<ActivityManager.RunningTaskInfo>
	 ActivityManager.getRunningTasks(30);
	 ActivityManager.getAppTasks();//没啥用

(3)获取获取最近任务使用应用的api: //返回 List<ActivityManager.RecentTaskInfo>
	ActivityManager.getRecentTasks(30, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
	UsageStatsManager.queryUsageStats()//没啥用

(4)获取目前存活的进程应用使用的api://返回 List<ActivityManager.RunningAppProcessInfo>
	ActivityManager.getRunningAppProcesses();

上面就是Activity应用信息的相关api;

最近任务的应用列表,并不仅仅是指最近使用的应用,还需要有系统缓存或者有存活进程的应用才会出现在列表。

不同的获取方式返回的数据对象是不同,这个可以看看哪些数据是实际有用的。

2、查看ActivityManager内部类应用数据对象

主要查看 RunningTaskInfo、RecentTaskInfo、RunningAppProcessInfo 这三个对象有啥有用的信息:

(1) RunningTaskInfo:正在运行的任务信息

核心含义 :描述当前系统中 "活动任务栈" 的状态(一个任务是一系列关联 Activity 的集合,遵循 "后进先出" 的栈结构)。

有用信息

  • taskId:任务的唯一标识符,用于区分不同任务。
  • topActivity:任务栈顶的 ComponentName(包含包名和类名),即当前用户可见的最上层 Activity
  • baseActivity:任务栈底的 ComponentName,通常是任务的入口 Activity(如应用的 launcher Activity)。
  • numActivities:该任务中包含的 Activity 数量,反映任务的复杂度。
  • description:任务的描述信息(可能为 null),通常用于 UI 展示。
  • isRunning:任务是否处于运行状态(非暂停 / 停止)。
  • priority:任务的优先级(数值越低优先级越高),影响系统内存不足时的回收策略。

用途 :可用于监控当前活跃的任务栈结构、判断前台应用(通过 topActivity)、分析任务切换流程等。

(2) RecentTaskInfo:最近任务信息

核心含义:描述用户最近交互过的任务(包含已退出但仍保留在 "最近任务列表" 中的任务),用于系统 "最近任务" 界面(如多任务切换界面)的展示。

有用信息

  • taskId:任务的唯一标识符(与 RunningTaskInfotaskId 对应)。
  • baseIntent:启动该任务的基础 Intent,可用于重新打开任务(如用户点击最近任务时,系统通过此 Intent 重启任务)。
  • lastActiveTime:任务最后一次活跃的时间戳,用于排序最近任务列表。
  • description:任务的描述文本(可能包含 Activity 标签)。
  • icon:任务的图标(通常是应用的图标)。
  • label:任务的显示名称(通常是应用名称或 Activity 标签)。
  • isPersisted:任务是否被持久化(即使进程被销毁,仍保留在最近列表中)。

用途:主要用于构建自定义的最近任务列表、判断用户最近使用的应用、实现类似系统多任务切换的功能。

(3) RunningAppProcessInfo:正在运行的应用进程信息

核心含义 :描述当前系统中正在运行的应用进程(一个进程可能包含多个组件,如 ActivityService 等)。

有用信息

  • pid:进程的 ID(进程唯一标识)。
  • uid:进程所属的用户 ID(用于权限管理,同一应用通常共享相同 uid)。
  • processName:进程名称(通常与应用包名一致,或为包名 + 进程别名,如 com.example.app:background)。
  • importance:进程的重要性等级(如 IMPORTANCE_FOREGROUND 表示前台进程,IMPORTANCE_BACKGROUND 表示后台进程),决定了内存不足时系统优先回收哪个进程。
  • pkgList:该进程中包含的所有应用包名(一个进程可能运行多个包的组件,如共享进程的应用)。
  • status:进程的状态(如 STATUS_RUNNING 表示正在运行,STATUS_PAUSED 表示暂停)。
  • lastTrimLevel:最近一次内存修剪的等级(反映系统内存压力,数值越高压力越大)。

用途:可用于监控系统进程状态、分析应用内存占用、判断进程是否存活、实现进程管理工具(如清理后台进程)等。

总结

  • RunningTaskInfo 聚焦于 "任务栈" 的 Activity 组织关系,反映当前活跃的界面流程;
  • RecentTaskInfo 聚焦于用户最近交互的任务,用于任务切换场景;
  • RunningAppProcessInfo 聚焦于底层进程的运行状态,反映系统资源分配和进程优先级。

这三个内部类的源码都是可以在Android Studio/系统源码查看包含的具体信息。

3、正在运行的应用进程等级 RunningAppProcessInfo.importance

进程是否被终止,取决于系统的 "内存管理策略",核心依据是进程重要性等级(由高到低):

进程类型 重要性等级 存活优先级 典型场景
前台进程 IMPORTANCE_FOREGROUND //100 最高 有可见 Activity 或前台服务
可见进程 IMPORTANCE_VISIBLE //200 有部分可见的 Activity(如弹窗)
服务进程 IMPORTANCE_SERVICE //300 有后台服务运行
缓存进程 IMPORTANCE_CACHED //400 无活跃组件,仅作缓存

这里可以看到进程重要性等级的值越低,存活优先级越高,应用进程存活的概率越大。

当系统内存不足时,会按照 "从低优先级到高优先级" 的顺序回收进程:

先杀缓存进程,再杀服务进程,最后才会考虑可见 / 前台进程。

因此,即使 Activity 销毁,只要进程是 "服务进程" 或 "缓存进程",就可能继续存活。

这个也是为啥只有Activity组件的应用退出界面后,系统还是能检测到它在应用进程和最近任务进程列表;

如果对该应用进行forceStop,那么它就不会被检测在应用进程和最近任务进程列表。

进程等级类别定义:

复制代码
public class ActivityManager {
    private static String TAG = "ActivityManager";

	public static final int IMPORTANCE_FOREGROUND = 100;

        /**
         * Constant for {@link #importance}: This process is running something
         * that is actively visible to the user, though not in the immediate
         * foreground.  This may be running a window that is behind the current
         * foreground (so paused and with its state saved, not interacting with
         * the user, but visible to them to some degree); it may also be running
         * other services under the system's control that it inconsiders important.
         */
        public static final int IMPORTANCE_VISIBLE = 200;

        /**
         * Constant for {@link #importance}: This process contains services
         * that should remain running.  These are background services apps have
         * started, not something the user is aware of, so they may be killed by
         * the system relatively freely (though it is generally desired that they
         * stay running as long as they want to).
         */
        public static final int IMPORTANCE_SERVICE = 300;

        /**
         * Constant for {@link #importance}: This process process contains
         * cached code that is expendable, not actively running any app components
         * we care about.
         */
        public static final int IMPORTANCE_CACHED = 400;
        
        ...//中间还有些100多,200多的值可能没那么重要。
}        

4、查看ActivityManager内部类应用数据对象打印日志

三个应用数据对象的定义如下:

复制代码
@SystemService(Context.ACTIVITY_SERVICE)
public class ActivityManager {
    private static String TAG = "ActivityManager";
    ...
	public static class RunningTaskInfo extends TaskInfo implements Parcelable {...}
	public static class RecentTaskInfo extends TaskInfo implements Parcelable {...}
	public static class RunningAppProcessInfo implements Parcelable {...}
}

上面三个内部类本身没有实现toString()方法;

但是 TaskInfo 有 toString()包含的内容还挺多的,比如:

复制代码
public class TaskInfo {
    private static final String TAG = "TaskInfo";

	@Override
    public String toString() {
        return "TaskInfo{userId=" + userId + " taskId=" + taskId
                + " displayId=" + displayId
                + " isRunning=" + isRunning
                + " baseIntent=" + baseIntent + " baseActivity=" + baseActivity
                + " topActivity=" + topActivity + " origActivity=" + origActivity
                + " realActivity=" + realActivity
                + " numActivities=" + numActivities
                + " lastActiveTime=" + lastActiveTime
                + " supportsMultiWindow=" + supportsMultiWindow
                + " resizeMode=" + resizeMode
                + " isResizeable=" + isResizeable
                + " minWidth=" + minWidth
                + " minHeight=" + minHeight
                + " defaultMinSize=" + defaultMinSize
                + " token=" + token
                + " topActivityType=" + topActivityType
                + " pictureInPictureParams=" + pictureInPictureParams
                + " shouldDockBigOverlays=" + shouldDockBigOverlays
                + " launchIntoPipHostTaskId=" + launchIntoPipHostTaskId
                + " lastParentTaskIdBeforePip=" + lastParentTaskIdBeforePip
                + " displayCutoutSafeInsets=" + displayCutoutInsets
                + " topActivityInfo=" + topActivityInfo
                + " launchCookies=" + launchCookies
                + " positionInParent=" + positionInParent
                + " parentTaskId=" + parentTaskId
                + " isFocused=" + isFocused
                + " isVisible=" + isVisible
                + " isSleeping=" + isSleeping
                + " topActivityInSizeCompat=" + topActivityInSizeCompat
                + " topActivityEligibleForLetterboxEducation= "
                        + topActivityEligibleForLetterboxEducation
                + " topActivityLetterboxed= " + isLetterboxDoubleTapEnabled
                + " isFromDoubleTap= " + isFromLetterboxDoubleTap
                + " topActivityLetterboxVerticalPosition= " + topActivityLetterboxVerticalPosition
                + " topActivityLetterboxHorizontalPosition= "
                        + topActivityLetterboxHorizontalPosition
                + " topActivityLetterboxWidth=" + topActivityLetterboxWidth
                + " topActivityLetterboxHeight=" + topActivityLetterboxHeight
                + " locusId=" + mTopActivityLocusId
                + " displayAreaFeatureId=" + displayAreaFeatureId
                + " cameraCompatControlState="
                        + cameraCompatControlStateToString(cameraCompatControlState)
                + "}";
    }
...

}

所以 RunningTaskInfo 和 RecentTaskInfo 可以用 toString() 打印查看所有信息看看哪些有用;

RunningAppProcessInfo 就对比上面有用的信息数据逐个打印想要的数据进行查看即可。

RunningTaskInfo.toString()打印的示例日志,如下:

复制代码
前台任务ID: 102 
TaskInfo{userId=0 taskId=102 displayId=0 isRunning=true 
	baseIntent=Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.android.settings/.Settings } 	 	 baseActivity=ComponentInfo{com.android.settings/
	com.android.settings.homepage.SettingsHomepageActivity} 	topActivity=ComponentInfo{com.android.settings/
	com.android.settings.SubSettings} origActivity=ComponentInfo{com.android.settings/
	com.android.settings.Settings} realActivity=ComponentInfo{com.android.settings/
	com.android.settings.homepage.SettingsHomepageActivity} 
	numActivities=3 lastActiveTime=9088708 supportsMultiWindow=true 
	resizeMode=1 isResizeable=true minWidth=-1 minHeight=-1 
	defaultMinSize=180 
	token=WCT{android.window.IWindowContainerToken$Stub$Proxy@76ece20} 
	topActivityType=1 pictureInPictureParams=null shouldDockBigOverlays=false
    launchIntoPipHostTaskId=-1 lastParentTaskIdBeforePip=-1 
    displayCutoutSafeInsets=null 
    topActivityInfo=ActivityInfo{9bbb3d9 com.android.settings.SubSettings} 
    launchCookies=[] positionInParent=Point(0, 0) parentTaskId=-1 
    isFocused=false isVisible=false isVisibleRequested=false 
    isSleeping=false topActivityInSizeCompat=false
    topActivityEligibleForLetterboxEducation= false 
    isLetterboxDoubleTapEnabled= false 
    topActivityEligibleForUserAspectRatioButton= false 
    topActivityBoundsLetterboxed= false 
    isFromLetterboxDoubleTap= false 
    topActivityLetterboxVerticalPosition= -1 
    topActivityLetterboxHorizontalPosition= -1 
    topActivityLetterboxWidth=-1 topActivityLetterboxHeight=-1 
    isUserFullscreenOverrideEnabled=false 
    isTopActivityTransparent=false 
    locusId=null displayAreaFeatureId=1 cameraCompatControlState=hidden
}

5、清除后台任务代码

一般只遍历当前后台任务,获取包名,然后根据需求清除一部分或者全部的应用

forceStopPackage 方法是隐藏的,所以需要反射:

复制代码
    public static void forceStopPackage(Context context , String packageName) {
        try {
            ActivityManager mActivityManager = (ActivityManager)
                    context.getSystemService(Context.ACTIVITY_SERVICE);
            mActivityManager.forceStopPackage(packageName);
            Method method;
            method = Class.forName("android.app.ActivityManager").getMethod("forceStopPackage", String.class);
            method.invoke(mActivityManager, packageName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

上面就是根据包名强制停止应用程序的示例代码;

被停止的应用程序,后续不会在最近任务列表和存活进程列表中出现。

上面内容还是比较多的,可能不一定都有用,可以收藏后续按需查看吧。

相关推荐
失因20 分钟前
SELinux 安全机制详解与管理
linux·运维·安全·centos
tangweiguo0305198723 分钟前
Flutter 与 Android NDK 集成实战:实现高性能原生功能
android·flutter
CtrlZ学习录1 小时前
安全引导功能及ATF的启动过程(五)
linux·安全·架构·开源·可信计算技术
_祝你今天愉快2 小时前
Android SurfaceView & TextureView
android·性能优化
蝸牛ちゃん2 小时前
《计算机信息系统安全保护等级划分准则》(GB17859-1999):中国信息安全等级保护的基石
安全·信息安全·系统安全·gb17859-1999·信息安全等级·安全等级划分
q5507071773 小时前
uniapp/uniappx实现图片或视频文件选择时同步告知权限申请目的解决华为等应用市场上架审核问题
android·图像处理·uni-app·uniapp·unix
李新_4 小时前
一个复杂Android工程开发前我们要考虑哪些事情?
android·程序员·架构
casual_clover5 小时前
Android 中解决 Button 按钮背景色设置无效的问题
android·button