文章目录
前言
本文主要是讲解系统应用如何实现栈顶应用变化监听以及存在多个屏幕时如何准确获取顶部Activity
提示:以下是本篇文章正文内容,下面案例可供参考
一、监听栈顶应用变化
1.引入库
只列出重要部分:
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.IProcessObserver;
import android.app.TaskStackListener;
2.设置监听
只贴出关键代码,可根据需要自行修改添加:
private final IActivityManager mAm;
private final ProcessObserver mProcessObserver;
private final TaskListener mTaskListener;
private final HandlerThread mMonitorHandlerThread;
private final ActivityMonitorHandler mHandler;
public SystemActivityMonitoringService(Context context) {
mContext = context;
mMonitorHandlerThread = new HandlerThread(TAG);
mMonitorHandlerThread.start();
mHandler = new ActivityMonitorHandler(mMonitorHandlerThread.getLooper());
mProcessObserver = new ProcessObserver();
mTaskListener = new TaskListener();
mAm = ActivityManager.getService();
mAtm = ActivityManager.getTaskService();
// Monitoring both listeners are necessary as there are cases where one listener cannot
// monitor activity change.
try {
mAm.registerProcessObserver(mProcessObserver);
mAm.registerTaskStackListener(mTaskListener);
} catch (Exception e) {
Log.e(TAG, "cannot register activity monitoring", e);
throw new RuntimeException(e);
}
mHandler.requestUpdatingTask();
}
@SuppressLint("DefaultLocale")
private class ProcessObserver extends IProcessObserver.Stub {
@Override
public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
/*
foregroundActivities表示应用是否在前台,在前台为true,在后台为false,
缺点:应用已在前台,但是应用内Activity变化则不会继续触发
快速切换两个应用,可能不会触发
*/
LOG(String.format("onForegroundActivitiesChanged uid %d pid %d fg %b",
uid, pid, foregroundActivities));
if (foregroundActivities){
mHandler.requestForegroundActivitiesChanged(pid, uid, foregroundActivities);
}
}
@Override
public void onForegroundServicesChanged(int pid, int uid, int fgServiceTypes) {
LOG(String.format("onForegroundServicesChanged uid %d pid %d %d", uid, pid, fgServiceTypes));
}
@Override
public void onProcessDied(int pid, int uid) {
/*
应用进程死掉才会出发,应用闪退或者被系统杀死都会触发
*/
LOG(String.format("onProcessDied uid %d pid %d", uid, pid));
// mHandler.requestProcessDied(pid, uid);
}
}
private class TaskListener extends TaskStackListener {
@Override
public void onTaskStackChanged() {
LOG("onTaskStackChanged");
/*
只要Activity变化,就会触发
缺点:第一次打开应用的时候,可能不会触发
*/
mHandler.requestUpdatingTask();
}
}
该处使用的url网络请求的数据。
二、获取顶部Activity
1.引入库
只列出重要部分:
import android.app.ActivityManager.StackInfo;
2.获取顶部Activity
1.getRunningTasks
此方法非系统应用可以使用,获取数量只能是1,不能获取全部的堆栈内的Activity,但系统应用则可以,缺点是多屏情况下,栈顶会出现与现实的不对应。使用方法如下:
ActivityManager manager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
//获取正在运行的task列表,其中1表示最近运行的task,通过该数字限制列表中task数目,最近运行的靠前
List<ActivityManager.RunningTaskInfo> runningTaskInfos = manager.getRunningTasks(1);
2.getAllStackInfos
此方法可以获取到当前每个屏幕内正在显示的Activity,缺点是,在一个屏幕内有多个虚拟屏幕时,快速切换应用,过程中容易出现获取错误,但是最终结果基本是正确,对过程要求不高的可以使用。
StackInfo类包含应用的很多信息:例如
ComponentName topActivity;
taskId;
displayId; //所在屏幕的id
position;
关键代码:
List<StackInfo> infos;
try {
infos = mAm.getAllStackInfos();
} catch (Exception e) {
Log.e(TAG, "cannot getTasks", e);
return;
}
//删选无用的信息
for (StackInfo info : infos) {
if (info.taskNames.length == 0 || !info.visible ) { // empty stack or not shown
continue;
}
//新建一个对象储存数据
}
3.getFocusedStackInfo
此方法可以获取到当前那个屏幕获取到焦点,即使多个屏幕下也只返回一个Activity,在屏幕内多次切换时判断不容易出错,缺点是,在切换时,瞬间点击其他屏幕,返回的不是新打开的应用,而是点击的那个屏幕内应用。可与方法2结合使用。
StackInfo info;
try {
info = mAm.getFocusedStackInfo();
} catch (Exception e) {
Log.e(TAG, "cannot getTasks", e);
isUpdateTasks = false;
return;
}
三、总结
针对一个屏幕内存在多个虚拟屏幕的情况下,如何完美的获取栈顶应用是一件复杂的事情,很容易出现想不到的获取错误,只能不断去完善去规避。