Android在finish结束应用后,之前占用内存不会立即被释放出来。在内存不足的时候,我们可以recent按钮清理后台的应用。点击recent按钮,界面上会显示所有有界面后台的task的栈顶缩微图(Launch不会显示)
下面是包含关系
当我们设置MainActivity为singleInstance时:
TaskRecord{1175cb4 #59 A=com.can.keycodeddetected U=0 StackId=1 sz=1}
Run #1: ActivityRecord{f3ddef9 u0 com.can.keycodeddetected/.MainActivity t59}
TaskRecord{c6b59bc #58 A=com.can.keycodeddetected U=0 StackId=1 sz=1}
Run #0: ActivityRecord{3c56e71 u0 com.can.keycodeddetected/.autofilltest.AutoFillActivity t58}
当我们设置MainActivity为standard时:
TaskRecord{d0f6fec #60 A=com.can.keycodeddetected U=0 StackId=1 sz=2}
Run #1: ActivityRecord{1ca3948 u0 com.can.keycodeddetected/.MainActivity t60}
Run #0: ActivityRecord{b07d861 u0 com.can.keycodeddetected/.autofilltest.AutoFillActivity t60}
mResumedActivity: ActivityRecord{1ca3948 u0 com.can.keycodeddetected/.MainActivity t60}
从上面可以看出singleInstance模式下,会有两个TaskRecord,分别是#58,#59,在standard就只有一个#60。
获取后台任务,清理后台,以及获取内存数据,都是在AMS以及相关服务里面完成的。
如获取内存:
获取AMS代理对象:
ActivityManager mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
获取内存:
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
mAm.getMemoryInfo(memoryInfo);
其他api后台分析
PhoneWindowManager响应按键事件(如何是虚拟按键直接看后面)
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
....
....
showRecentApps(true, false);
....
}
private void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) {
mPreloadedRecentApps = false; // preloading no longer needs to be canceled
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
if (statusbar != null) {
statusbar.showRecentApps(triggeredFromAltTab, fromHome);
}
}
StatusBarManagerInternal是StatusbarManagerService的内部对象,位于系统进程。
frameworks/base//services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@Override
public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) {
if (mBar != null) {
try {
mBar.showRecentApps(triggeredFromAltTab, fromHome);
} catch (RemoteException ex) {}
}
}
这里的mBar对象是SystemUI调用StatusBarManagerService服务的registerStatusBar传递进来的,使得SystemUI与StatusBarManagerService能够双向通讯,使得StatusBarManagerService不仅仅能被请求,还能主动请求SystemUI这个客户端。
(注意:Binder是Android一种跨进程的通讯方式,属于客户服务端方式,client主动请求,service被动接受,然后返回,如果需要双方都能够主动请求,必须传递一个binder过去,让两个对象互相作为对方的客户端)
上面的mBar对象对应的是SystemUI里面的CommandQueue
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
从CommandQueue大概走如下流程
到达RecentsActivity,就是显示历史任务
frameworks/base/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
这里面有3个操作
清理后台stack,清除指定stack或全部stack
在处理RecentsActivity.java处理(省略清除按钮到这个处理方法的步骤)
frameworks/base/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
public final void onBusEvent(DeleteTaskDataEvent event) {
// Remove any stored data from the loader
RecentsTaskLoader loader = Recents.getTaskLoader();
loader.deleteTaskData(event.task, false);
// Remove the task from activity manager
SystemServicesProxy ssp = Recents.getSystemServices();
// SPRD: Bug 692452 new feature of lock recent apps
if(!event.task.isTaskLocked){
ssp.removeTask(event.task.key.id);
}
}
frameworks/base/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
/** Removes the task */
public void removeTask(final int taskId) {
if (mAm == null) return;
if (RecentsDebugFlags.Static.EnableMockTasks) return;
// Remove the task.
mUiOffloadThread.submit(() -> {
mAm.removeTask(taskId);
});
}
可以看到会调用AMS的removeTask的方法,这个是系统api,不再进行分析。
获取手机运存,并显示出来
frameworks/base/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
/* SPRD: new feature of showing memory tip @{ */
public ActivityManager.MemoryInfo getMemoryInfo() {
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
mAm.getMemoryInfo(memoryInfo);
return memoryInfo;
}
调用mAm.getMemoryInfo(memoryInfo);获取手机当前内存。
获取后他的stack,并显示处理
frameworks/base/packages/SystemUI//src/com/android/systemui/recents/RecentsActivity.java
private void reloadStackView() {
// If the Recents component has preloaded a load plan, then use that to prevent
// reconstructing the task stack
//看做RecentsTaskLoader loader = new RecentsTaskLoader();
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsTaskLoadPlan loadPlan = RecentsImpl.consumeInstanceLoadPlan();
if (loadPlan == null) {
//看做RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan();
loadPlan = loader.createLoadPlan(this);
}
// Start loading tasks according to the load plan
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
if (!loadPlan.hasTasks()) {
loader.preloadTasks(loadPlan, launchState.launchedToTaskId,
!launchState.launchedFromHome && !launchState.launchedViaDockGesture);//1
}
RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
loadOpts.runningTaskId = launchState.launchedToTaskId;
loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
loader.loadTasks(this, loadPlan, loadOpts);//2
TaskStack stack = loadPlan.getTaskStack();
mRecentsView.onReload(stack, mIsVisible);
....
}
上面主要通过RecentsTaskLoader和RecentsTaskLoadPlan获取历史任务。在1,2处,
//加载最终调AMS加载task。Rencenttaskinfo 转task操作
loader.preloadTasks(loadPlan, launchState.launchedToTaskId,
!launchState.launchedFromHome && !launchState.launchedViaDockGesture);
//获取loader保存的task,并配置图标等等
loader.loadTasks(this, loadPlan, loadOpts);
这两个类互相调用,流程如下
在preloadTasks和loadTasks方法里面做了许多判断,没有再具体分析。比如androd Go存在修改时间无法显示历史任务的问题,就是这两个方法中某一个条件导致的,
参考:https://www.jianshu.com/p/5133f012f21b
我们要在SystemUi里面自己写一个清理后台的功能,比如在quick Setting中清理,可以把代码剥离出来使用
封装两个方法直接使用:
private ArrayList reloadStack() {
// If the Recents component has preloaded a load plan, then use that to prevent
// reconstructing the task stack
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsTaskLoadPlan loadPlan = RecentsImpl.consumeInstanceLoadPlan();
if (loadPlan == null) {
loadPlan = loader.createLoadPlan(mContext);
}
// Start loading tasks according to the load plan
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
if (!loadPlan.hasTasks()) {
loader.preloadTasks(loadPlan, launchState.launchedToTaskId,
!launchState.launchedFromHome && !launchState.launchedViaDockGesture);
}
RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
loadOpts.runningTaskId = launchState.launchedToTaskId;
loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
loader.loadTasks(mContext, loadPlan, loadOpts);
TaskStack stack = loadPlan.getTaskStack();
// Keep track of the total stack task count
return stack.getStackTasks();
}
private void removeTask(Task task) {
// Remove any stored data from the loader
RecentsTaskLoader loader = Recents.getTaskLoader();
loader.deleteTaskData(task, false);
// Remove the task from activity manager
SystemServicesProxy ssp = Recents.getSystemServices();
Log.i("wangcan",task+">remove #"+task.key.id);
// SPRD: Bug 692452 new feature of lock recent apps
if(!task.isTaskLocked){
ssp.removeTask(task.key.id);
}
}
使用方法:
protected void startClearAllTask() {
mHost.collapsePanels();
ArrayList allTasks = reloadStack();
int count = allTasks.size();
for(int i = 0;i < count;i++) {
Task oneTask = allTasks.get(i);
removeTask(oneTask);
}
}