Android 12系统源码_页面管理(四)获取系统当前最上层的Activity信息

前言

很多应用开发人员,在日常开发过程中,经常会遇到一些需求,例如需要知道当前最上层的Activity是哪个,并结合这个Activity的名称来完成一些特定场景的需求。最简单的方法,是在创建Activity的时候将该Actvity存储到一个集合中,而当Activity销毁的时候,再将该Activity从集合中移除,这种方案虽然能够获取自己应用当前最上层的Activity是那个,但却无法获取除了自己应用以外的其他场景。犹豫谷歌为系统开发提供了特定的API,作为系统开发人员,我们完全可以通过这些API实时获取当前最上层的Activity信息,并将这些信息同步给系统应用。本篇文章我们将会结合Android12的系统源码,来探讨一下如何通过系统内部API实时获取当前系统最上层的Activity的信息,以及如何实时监听当前系统Activity栈信息的变化。

一、获取最上层的根任务信息

1、根任务是指包含一个或多个 Activity 的任务,并且没有父任务,根任务通常是由用户启动的应用程序或系统应用程序的主要任务。
在 Android 12 中,我们可以通过以下代码获取最上层的根任务信息。

IActivityManager ams = ActivityManager.getService(); //获取ActivityManagerService服务对象
List<ActivityManager.RunningTaskInfo> runningTasks = mAm.getTasks(1);;//调用getAllRootTaskInfos方法

首先调用ActivityManager的getService方法获取ActivityManagerService服务对象,然后调用该服务对象的getTasks(int maxNum) 方法,该方法会返回一个指定数量包含所有根任务信息的列表,每个根任务都有其任务 ID、根 Activity 的信息以及与之关联的其他活动堆栈。

2、来看下和RunningTaskInfo类相关的代码。

base/core/java/android/app/ActivityManager.java

public class ActivityManager {
   
    public static class RunningTaskInfo extends TaskInfo implements Parcelable {
   

        /**
         * 当前任务的唯一标识id
         * A unique identifier for this task.
         */
        @Deprecated
        public int id;

        /**
         * 当前任务状态的缩略图
         * Thumbnail representation of the task's current state.
         */
        @Deprecated
        public Bitmap thumbnail;

        /**
         * 当前任务的状态描述
         * Description of the task's current state.
         */
        @Deprecated
        public CharSequence description;

        /**
         * 当前任务中正在运行的Activity的数量
         * Number of activities that are currently running (not stopped and persisted) in this task.
         */
        @Deprecated
        public int numRunning;

    }
}

RunningTaskInfo类包含了当前任务对应的唯一标识ID、Bitmap类型的缩略图、状态描述以及该任务正运行的Activity的数量。

3、RunningTaskInfo继承自TaskInfo,继续来看下该类有哪些关键属性。

base/core/java/android/app/TaskInfo.java

public class TaskInfo {
   
    private static final String TAG = "TaskInfo";

    /**
     * 当前任务对应的用户id    
     * The id of the user the task was running as if this is a leaf task. The id of the current
     * running user of the system otherwise.
     * @hide
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public int userId;

    /**
     * 任务id
     * The identifier for this task.
     */
    public int taskId;

    /**
     * 此任务中是否有正在运行的Activity
     * Whether or not this task has any running activities.
     */
    public boolean isRunning;

    /**
     * 启动当前任务的Intent
     * The base intent of the task (generally the intent that launched the task). This intent can
     * be used to relaunch the task (if it is no longer running) or brought to the front if it is.
     */
    @NonNull
    public Intent baseIntent;

    /**
     * The component of the first activity in the task, can be considered the "application" of this
     * task.
     */
    @Nullable
    public ComponentName baseActivity;

    /**
     * 当前任务对应的Activity栈中的最上层正在显示的activity
     * The component of the top activity in the task, currently showing to the user.
     */
    @Nullable
    public ComponentName topActivity;

    /**
     * The component of the target activity if this task was started from an activity alias.
     * Otherwise, this is null.
     */
    @Nullable
    public ComponentName origActivity;

    /**
     * The component of the activity that started this task (may be the component of the activity
     * alias).
     * @hide
     */
    @Nullable
    public ComponentName realActivity;

    /**
     * The number of activities in this task (including running).
     */
    public int numActivities;

    /**
     * The last time this task was active since boot (including time spent in sleep).
     * @hide
     */
    @UnsupportedAppUsage
    public long lastActiveTime;

    /**
     * 当前任务对应的屏幕设备id
     * The id of the display this task is associated with.
     * @hide
     */
    public int displayId;

    /**
     * The feature id of {@link com.android.server.wm.TaskDisplayArea} this task is associated with.
     * @hide
     */
    public int displayAreaFeatureId = FEATURE_UNDEFINED;

    /**
     * The recent activity values for the highest activity in the stack to have set the values.
     * {@link Activity#setTaskDescription(android.app.ActivityManager.TaskDescription)}.
     */
    @Nullable
    public ActivityManager.TaskDescription taskDescription;

    /**
     * The locusId of the task.
     * @hide
     */
    @Nullable
    public LocusId mTopActivityLocusId;

    /**
     * 当前任务是否支持分屏
     * True if the task can go in the split-screen primary stack.
     * @hide
     */
    @UnsupportedAppUsage
    public boolean supportsSplitScreenMultiWindow;

    /**
     * 当前任务是否支持多窗口
     * Whether this task supports multi windowing modes based on the device settings and the
     * root activity resizability and configuration.
     * @hide
     */
    public boolean supportsMultiWindow;

    /**
     * The resize mode of the task. See {@link ActivityInfo#resizeMode}.
     * @hide
     */
    @UnsupportedAppUsage
    public int resizeMode;

    /**
     * The current configuration of the task.
     * @hide
     */
    @NonNull
    @UnsupportedAppUsage
    public final Configuration configuration = new Configuration();

    /**
     * Used as an opaque identifier for this task.
     * @hide
     */
    @NonNull
    public WindowContainerToken token;

    /**
     * 用于控制画中画模式的参数类
     * The PictureInPictureParams for the Task, if set.
     * @hide
     */
    @Nullable
    public PictureInPictureParams pictureInPictureParams;

    /**
     * The {@link Rect} copied from {@link DisplayCutout#getSafeInsets()} if the cutout is not of
     * (LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES, LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS),
     * {@code null} otherwise.
     * @hide
     */
    @Nullable
    public Rect displayCutoutInsets;

    /**
     * 当前任务最上层Activity的类型
     * The activity type of the top activity in this task.
     * @hide
     */
    public @WindowConfiguration.ActivityType int topActivityType;

    /**
     * The {@link ActivityInfo} of the top activity in this task.
     * @hide
     */
    @Nullable
    public ActivityInfo topActivityInfo;

    /**
     * Whether the direct top activity is in size compat mode on foreground.
     * @hide
     */
    public boolean topActivityInSizeCompat;

    /**
     * Whether this task is resizable. Unlike {@link #resizeMode} (which is what the top activity
     * supports), this is what the system actually uses for resizability based on other policy and
     * developer options.
     * @hide
     */
    public boolean isResizeable;

    /**
     * Relative position of the task's top left corner in the parent container.
     * @hide
     */
    public Point positionInParent;

    /**
     * The launch cookies associated with activities in this task if any.
     * @see ActivityOptions#setLaunchCookie(IBinder)
     * @hide
     */
    public ArrayList<IBinder> launchCookies = new ArrayList<>();

    /**
     * The identifier of the parent task that is created by organizer, otherwise
     * {@link ActivityTaskManager#INVALID_TASK_ID}.
     * @hide
     */
    public int parentTaskId;

    /**
     * 当前任务是否持有焦点
     * Whether this task is focused.
     * @hide
     */
    public boolean isFocused;

    /**
     * 当前任务是否可见
     * Whether this task is visible.
     * @hide
     */
    public boolean isVisible;

    /**
     * Whether this task is sleeping due to sleeping display.
     * @hide
     */
    public boolean isSleeping;

}

TaskInfo类中也包含了很多对于当前任务至关重要的信息:任务对应的用户id、任务id、任务中是否有运行的Activity、启动任务的Intent、任务对应的Activity栈最上层Activity,对应的屏幕设备id、任务是否支持分屏、任务是否支持多窗口、用于控制画中画模式的参数类、任务是否持有焦点、任务是否可见等。

二、实时监听Activity栈信息变化。

1、我们主要是通过调用ActivityManagerService的相关方法来实时监听Activity对应的任务栈的变化的,具体可以参考以下代码。

		   //获取ActivityManagerService的实例对象
           IActivityManager am = ActivityManager.getService();
           //调用registerTaskStackListener方法,注册监听任务栈变化的回调对象
           am.registerTaskStackListener(new TaskStackListener() {
   
                @Override
                public void onTaskStackChanged() throws RemoteException {
   
                    final ActivityManager.RunningTaskInfo runningTask;
                    try {
   
                    	//
                        List<ActivityManager.RunningTaskInfo> runningTasks = mAm.getTasks(1);
                        if (runningTasks == null) {
   
                            return;
                        }
                        runningTask = runningTasks.get(0);
                    } catch (RemoteException e) {
   
                        e.printStackTrace();
                        return;
                    }
                    if (runningTask == null) {
   
                        return;
                    }
                    int displayId = runningTask.displayId;
                    if (INVALID_DISPLAY != displayId && runningTask.topActivity != null) {
   
                        String key = "display_" + displayId + "_top_activity";
                        String packageName = runningTask.topActivity.getPackageName();
                        String activityName = runningTask.topActivity.getClassName();
                        String value = packageName + "/" + activityName;
                        boolean isUpdate = !value.equals(mHashMapDisplayTopActivity.get(key));
                        if (isUpdate) {
   
                            Log.d(TAG, "updateTasks: key = " + key + " value = " + value);
                            mHashMapDisplayTopActivity.put(key, value);
                        }
                    }  
                }
            });;

对以上代码做个简单介绍:

  • 我们可以通过调用ActivityManagerService的registerTaskStackListener方法注册回调对象,实时监听当前任务栈的变化。
  • 继续调用ActivityManagerService的getTasks方法,获取当前正在运行的所有任务,该方法返回一个类型为RunningTaskInfo的集合,关于RunningTaskInfo这个类,前面我们已经做过简单介绍了。
  • 最后结合RunningTaskInfo的相关属性,可以成功获取到当前系统中每个屏幕设备对应的最上层的Activity的包名和组件名称。

三、异常处理

不过在进行实测的时候发现,通过调用registerTaskStackListener方法注册的回调方法,有一定概率在页面发生切换的时候不会进行回调,比如应用异常崩溃,应用ANR被强制关闭等特殊情况,这个字段回调方法并没有执行,这就需要我们在这些事件发生的时候,通知AMS让其进行回调事件的调用。

最近更新

  1. TCP协议是安全的吗?

    2024-02-03 06:02:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-03 06:02:02       18 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-03 06:02:02       17 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-03 06:02:02       20 阅读

热门阅读

  1. Go的两阶段延迟执行

    2024-02-03 06:02:02       29 阅读
  2. 【精华文章】戊子仲秋的算法学习笔记汇总

    2024-02-03 06:02:02       35 阅读
  3. 【精华文章】戊子仲秋的编程学习笔记汇总

    2024-02-03 06:02:02       37 阅读
  4. leetcode - 1004. Max Consecutive Ones III

    2024-02-03 06:02:02       27 阅读
  5. Android 8.1 设置--声音中增加通话音量

    2024-02-03 06:02:02       29 阅读
  6. 计算机网络(第六版)复习提纲15

    2024-02-03 06:02:02       26 阅读
  7. ++i(前置自增)和 i++(后置自增)的区别

    2024-02-03 06:02:02       25 阅读
  8. 【前端插件工具】

    2024-02-03 06:02:02       30 阅读
  9. Vue.js 中父组件调用子组件的方法

    2024-02-03 06:02:02       35 阅读
  10. pip安装tf-gpu=2.4的bug解决方案

    2024-02-03 06:02:02       36 阅读
  11. wpf 消息传递

    2024-02-03 06:02:02       29 阅读
  12. Ubuntu 22.04 配置qtchooser默认启用 Qt6

    2024-02-03 06:02:02       32 阅读