【TaskStackListener】Android 中用于监听和响应任务栈

【TaskStackListener】Android 中用于监听和响应任务栈

  • [一、TaskStackListener 的主要功能和应用场景](#一、TaskStackListener 的主要功能和应用场景)
  • [二、TaskStackListener 的核心方法](#二、TaskStackListener 的核心方法)
  • [三、如何使用 TaskStackListener (仅限系统应用)](#三、如何使用 TaskStackListener (仅限系统应用))
  • 四、总结

TaskStackListener 是一个隐藏的 Android API,它为系统级应用提供了一套强大的机制,用于监听和响应任务栈 (Task Stack) 的变化。通俗地说,它就像一个安装在 Android 系统任务管理器中的"侦听器",能够实时感知应用的启动、切换、关闭等行为,并触发相应的回调。

然而,需要特别注意的是,TaskStackListener 是一个非公开的 API (Hidden API),这意味着 Google 并不保证其在不同 Android 版本间的稳定性和可用性,且不建议第三方应用程序直接使用。对于普通应用开发者而言,强行使用可能会导致应用在未来的系统更新中出现兼容性问题。

一、TaskStackListener 的主要功能和应用场景

对于有系统开发权限的开发者而言,TaskStackListener 在以下场景中非常有用:

  • 系统级界面定制: 例如,在车载系统或定制化的 Launcher 中,需要根据当前前台应用的变化来调整界面元素或执行特定逻辑。
  • 应用行为监控: 在一些特殊的系统工具中,可以用来监控特定应用的启动和关闭事件。(我看过的Launcher和SystemUI有类似的功能)
  • 多窗口和分屏管理: 在实现复杂的多窗口功能时,需要精确地感知任务栈的变动。

二、TaskStackListener 的核心方法

TaskStackListener 是一个抽象类,开发者需要继承它并重写其内部的方法来处理不同的任务栈变化事件。以下是一些关键的回调方法:

  • onTaskStackChanged(): 这是一个最通用和最频繁被触发的回调。每当任务栈发生任何变化,例如 Activity 的启动、销毁,任务的创建、移除或顺序改变,都会调用此方法。
  • onTaskCreated(int taskId, ComponentName componentName): 当一个新的任务被创建时调用。这通常发生在用户启动一个新应用或一个应用以新的任务栈方式启动一个 Activity 时。
  • onTaskRemoved(int taskId): 当一个任务被移除时调用。例如,用户从最近任务列表中划掉一个应用。
  • onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo): 当一个已存在的任务被切换到前台时调用。例如,用户通过最近任务列表切换回一个后台应用。
  • onActivityPinned(String packageName, int userId, int taskId, int displayId): 当一个 Activity 进入画中画 (Pinned) 模式时调用。
  • onActivityUnpinned(): 当一个 Activity 退出画中画模式时调用。
java 复制代码
/**
 * Classes interested in observing only a subset of changes using ITaskStackListener can extend
 * this class to avoid having to implement all the methods.
 *
 * @hide
 */
public abstract class TaskStackListener extends ITaskStackListener.Stub {

    /** Whether this listener and the callback dispatcher are in different processes. */
    private boolean mIsRemote = true;

    @UnsupportedAppUsage
    public TaskStackListener() {
    }

    /** Indicates that this listener lives in system server. */
    public void setIsLocal() {
        mIsRemote = false;
    }

    @Override
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void onTaskStackChanged() throws RemoteException {
    }

    @Override
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void onActivityPinned(String packageName, int userId, int taskId, int rootTaskId)
            throws RemoteException {
    }

    @Override
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void onActivityUnpinned() throws RemoteException {
    }

    @Override
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
            boolean clearedTask, boolean wasVisible) throws RemoteException {
    }

    @Override
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void onActivityForcedResizable(String packageName, int taskId, int reason)
            throws RemoteException {
    }

    @Override
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void onActivityDismissingDockedTask() throws RemoteException {
    }

    @Override
    public void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo,
            int requestedDisplayId) throws RemoteException {
        onActivityLaunchOnSecondaryDisplayFailed();
    }

    /**
     * @deprecated see {@link
     *         #onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo, int)}
     */
    @Deprecated
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void onActivityLaunchOnSecondaryDisplayFailed() throws RemoteException {
    }

    @Override
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo,
            int requestedDisplayId) throws RemoteException {
    }

    @Override
    public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
    }

    @Override
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void onTaskRemoved(int taskId) throws RemoteException {
    }

    @Override
    public void onTaskMovedToFront(RunningTaskInfo taskInfo)
            throws RemoteException {
        onTaskMovedToFront(taskInfo.taskId);
    }

    /**
     * @deprecated see {@link #onTaskMovedToFront(RunningTaskInfo)}
     */
    @Deprecated
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void onTaskMovedToFront(int taskId) throws RemoteException {
    }

    @Override
    public void onTaskRemovalStarted(RunningTaskInfo taskInfo)
            throws RemoteException {
        onTaskRemovalStarted(taskInfo.taskId);
    }

    /**
     * @deprecated see {@link #onTaskRemovalStarted(RunningTaskInfo)}
     */
    @Deprecated
    public void onTaskRemovalStarted(int taskId) throws RemoteException {
    }

    @Override
    public void onTaskDescriptionChanged(RunningTaskInfo taskInfo)
            throws RemoteException {
        onTaskDescriptionChanged(taskInfo.taskId, taskInfo.taskDescription);
    }

    /**
     * @deprecated see {@link #onTaskDescriptionChanged(RunningTaskInfo)}
     */
    @Deprecated
    public void onTaskDescriptionChanged(int taskId, ActivityManager.TaskDescription td)
            throws RemoteException {
    }

    @Override
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation)
            throws RemoteException {
    }

    @Override
    public void onTaskProfileLocked(RunningTaskInfo taskInfo, int userId)
            throws RemoteException {
        onTaskProfileLocked(taskInfo);
    }

    /**
     * @deprecated see {@link #onTaskProfileLocked(RunningTaskInfo, int)}
     */
    @Deprecated
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void onTaskProfileLocked(RunningTaskInfo taskInfo)
            throws RemoteException {
    }

    @Override
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) throws RemoteException {
        if (mIsRemote && snapshot != null && snapshot.getHardwareBuffer() != null) {
            // Preemptively clear any reference to the buffer
            snapshot.getHardwareBuffer().close();
        }
    }

    @Override
    public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo)
            throws RemoteException {
    }

    @Override
    public void onTaskDisplayChanged(int taskId, int newDisplayId) throws RemoteException {
    }

    @Override
    public void onRecentTaskListUpdated() throws RemoteException {
    }

    @Override
    public void onRecentTaskListFrozenChanged(boolean frozen) {
    }

    @Override
    public void onTaskFocusChanged(int taskId, boolean focused) {
    }

    @Override
    public void onTaskRequestedOrientationChanged(int taskId, int requestedOrientation) {
    }

    @Override
    public void onActivityRotation(int displayId) {
    }

    @Override
    public void onTaskMovedToBack(RunningTaskInfo taskInfo) {
    }

    @Override
    public void onLockTaskModeChanged(int mode) {
    }
}

三、如何使用 TaskStackListener (仅限系统应用)

对于拥有系统权限的应用,使用 TaskStackListener 的基本步骤如下:

  1. 定义一个继承自 TaskStackListener 的类:

    java 复制代码
    import android.app.TaskStackListener;
    import android.content.ComponentName;
    import android.os.RemoteException;
    import android.util.Log;
    
    public class MyTaskStackListener extends TaskStackListener {
        private static final String TAG = "MyTaskStackListener";
    
        @Override
        public void onTaskStackChanged() throws RemoteException {
            Log.d(TAG, "onTaskStackChanged");
            // 任务栈发生变化
        }
    
        @Override
        public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
            Log.d(TAG, "onTaskCreated: taskId=" + taskId + ", componentName=" + componentName);
            // 新任务创建
        }
    
        @Override
        public void onTaskRemoved(int taskId) throws RemoteException {
            Log.d(TAG, "onTaskRemoved: taskId=" + taskId);
            // 任务被移除
        }
    
        @Override
        public void onTaskMovedToFront(int taskId) throws RemoteException {
             Log.d(TAG, "onTaskMovedToFront: taskId=" + taskId);
             // 任务被切换到前台
        }
    }
  2. 在系统服务中注册监听器:

    通常,这需要在具有系统权限的服务(例如系统UI或自定义的系统服务)中完成。

    java 复制代码
    import android.app.ActivityManager;
    import android.content.Context;
    
    public class MySystemMonitoringService extends Service {
        private MyTaskStackListener mTaskStackListener;
    
        @Override
        public void onCreate() {
            super.onCreate();
            mTaskStackListener = new MyTaskStackListener();
            try {
                ActivityManager.getService().registerTaskStackListener(mTaskStackListener);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            if (mTaskStackListener != null) {
                try {
                    ActivityManager.getService().unregisterTaskStackListener(mTaskStackListener);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        // ... 其他服务相关的代码
    }

关于注册监听还可以通过 ActivityTaskManager,Android 框架进行了重构,将窗口管理和活动生命周期相关的核心逻辑分离到了专门的 ActivityTaskManager 中,而 ActivityManager 则更多地保留了应用层面的信息管理功能。

java 复制代码
IBinder binder = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
IActivityTaskManager atmService = IActivityTaskManager.Stub.asInterface(binder);
atmService .registerTaskStackListener(mTaskStackListener);

四、总结

TaskStackListener 是一个强大的工具,但它的使用范围被严格限制在系统应用层面。对于广大的第三方应用开发者而言,了解其存在和功能有助于更深入地理解 Android 的任务管理机制,但在实际开发中应避免直接使用。

需要特别指出的是,从 Android 12 开始,Google 对任务和窗口管理进行了重构。TaskStackListener 的部分功能和概念已经被新的 API TaskOrganizer 所取代。

TaskOrganizer 是一个更为强大和灵活的接口,它允许应用(同样需要特定权限)直接组织和控制任务的显示,例如在自定义的 Launcher 中实现对应用窗口的管理。对于希望在较新 Android 版本上实现类似功能开发者来说,应该研究和使用 TaskOrganizer

相关推荐
s***11704 小时前
Mysql convert函数、convert用法、字符串转数字、字符串转日期、类型转换函数
android·数据库·mysql
n***26564 小时前
【MySQL】MVCC详解, 图文并茂简单易懂
android·数据库·mysql
程序猿陌名!5 小时前
Android-EDLA RK3576谷歌ATTESTION-KEY从申请到烧录以及验证谷歌认证标志全流程
android
安卓理事人5 小时前
安卓版本升级功能
android
s***35305 小时前
怎么下载安装yarn
android·前端·后端
z***94845 小时前
使用rustDesk搭建私有远程桌面
android·前端·后端
q***06295 小时前
【细如狗】记录一次使用MySQL的Binlog进行数据回滚的完整流程
android·数据库·mysql
0***86335 小时前
图文详述:MySQL的下载、安装、配置、使用
android·mysql·adb
9***44635 小时前
SQLyog安装配置(注册码)连接MySQL
android·mysql·adb
o***11145 小时前
【MySQL】MySQL库的操作
android·数据库·mysql