SystemUI RecentsActivity 分析

SystemUI RecentsActivity 分析

功能描述

Android在finish结束应用后,之前占用内存不会立即被释放出来。在内存不足的时候,我们可以recent按钮清理后台的应用。点击recent按钮,界面上会显示所有有界面后台的task的栈顶缩微图(Launch不会显示)

预先了解

ActivityStack,ActivityRecord,TaskRecord关系

  1. ActivityStack则是用来管理TaskRecord的,包含了多个TaskRecord。
  2. 一个TaskRecord由一个或者多个ActivityRecord组成,这就是我们常说的任务栈,具有后进先出的特点
  3. ActivityRecord对应一个Activity实例

下面是包含关系

->:包含
SystemUI RecentsActivity 分析_第1张图片

  • 问题1:什么情况才会有多个TaskRecord
    当跳转的下一个界面的启动模式是 android:launchMode=“singleInstance”,应用会产生新的TaskRecord
    可以通过 adb shell dumpsys activity 抓取后台task

当我们设置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。

  • 问题2:(一个应用内)点击recent按钮显示的是哪个TaskRecord,如果清除后台,是清除的TaskRecord还是ActivityStack
    通过应用,我们可以发现recent按钮,只会显示最后这个应用的界面(runningtask),当我们清除这个应用后台,整个应用都清除了,所以:点击recent按钮显示的是最后运行的TaskRecord,清除后台,是清除的ActivityStack,因为只有一个taskrecord显示在历史记录,如果是清除的TaskRecord,那么必须显示所有的TaskRecord,这样显然不合理,对于用户而言,用户不关心TaskRecord,用户只想把整个应用都干掉。

ActivityManager

获取后台任务,清理后台,以及获取内存数据,都是在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大概走如下流程

SystemUI RecentsActivity 分析_第2张图片

历史任务活动加载View和stack

到达RecentsActivity,就是显示历史任务
frameworks/base/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
这里面有3个操作

  1. 清理后台stack,清除指定stack或全部stack
  2. 获取手机运存,并显示出来
  3. 获取后他的stack,并显示处理

清理后台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);

这两个类互相调用,流程如下

SystemUI RecentsActivity 分析_第3张图片
在preloadTasks和loadTasks方法里面做了许多判断,没有再具体分析。比如androd Go存在修改时间无法显示历史任务的问题,就是这两个方法中某一个条件导致的,
参考:https://www.jianshu.com/p/5133f012f21b

扩展

我们要在SystemUi里面自己写一个清理后台的功能,比如在quick Setting中清理,可以把代码剥离出来使用

封装两个方法直接使用:

  1. 获取所有task
    返回所有后台能清理的task
    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();
   }

  1. 清理指定的task
   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);
        }
    }

你可能感兴趣的:(SystemUI详解)