本文主要是介绍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.*/@Deprecatedpublic int id;/*** 当前任务状态的缩略图* Thumbnail representation of the task's current state.*/@Deprecatedpublic Bitmap thumbnail;/*** 当前任务的状态描述* Description of the task's current state.*/@Deprecatedpublic CharSequence description;/*** 当前任务中正在运行的Activity的数量* Number of activities that are currently running (not stopped and persisted) in this task.*/@Deprecatedpublic 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.*/@NonNullpublic Intent baseIntent;/*** The component of the first activity in the task, can be considered the "application" of this* task.*/@Nullablepublic ComponentName baseActivity;/*** 当前任务对应的Activity栈中的最上层正在显示的activity* The component of the top activity in the task, currently showing to the user.*/@Nullablepublic ComponentName topActivity;/*** The component of the target activity if this task was started from an activity alias.* Otherwise, this is null.*/@Nullablepublic ComponentName origActivity;/*** The component of the activity that started this task (may be the component of the activity* alias).* @hide*/@Nullablepublic 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*/@UnsupportedAppUsagepublic 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)}.*/@Nullablepublic ActivityManager.TaskDescription taskDescription;/*** The locusId of the task.* @hide*/@Nullablepublic LocusId mTopActivityLocusId;/*** 当前任务是否支持分屏* True if the task can go in the split-screen primary stack.* @hide*/@UnsupportedAppUsagepublic 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*/@UnsupportedAppUsagepublic int resizeMode;/*** The current configuration of the task.* @hide*/@NonNull@UnsupportedAppUsagepublic final Configuration configuration = new Configuration();/*** Used as an opaque identifier for this task.* @hide*/@NonNullpublic WindowContainerToken token;/*** 用于控制画中画模式的参数类* The PictureInPictureParams for the Task, if set.* @hide*/@Nullablepublic 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*/@Nullablepublic 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*/@Nullablepublic 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() {@Overridepublic 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让其进行回调事件的调用。
这篇关于Android 12系统源码_页面管理(四)获取系统当前最上层的Activity信息的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!