SystemUI RecentsActivity 分析

SystemUI RecentsActivity 分析

功能描述

Android在finish結束應用後,之前佔用內存不會立即被釋放出來。在內存不足的時候,我們可以recent按鈕清理後臺的應用。點擊recent按鈕,界面上會顯示所有有界面後臺的task的棧頂縮微圖(Launch不會顯示)

預先了解

ActivityStack,ActivityRecord,TaskRecord關係

  1. ActivityStack則是用來管理TaskRecord的,包含了多個TaskRecord。
  2. 一個TaskRecord由一個或者多個ActivityRecord組成,這就是我們常說的任務棧,具有後進先出的特點
  3. ActivityRecord對應一個Activity實例

下面是包含關係

->:包含
在這裏插入圖片描述

  • 問題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大概走如下流程

在這裏插入圖片描述

歷史任務活動加載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);

這兩個類互相調用,流程如下

在這裏插入圖片描述
在preloadTasks和loadTasks方法裏面做了許多判斷,沒有再具體分析。比如androd Go存在修改時間無法顯示歷史任務的問題,就是這兩個方法中某一個條件導致的,
參考:https://www.jianshu.com/p/5133f012f21b

擴展

我們要在SystemUi裏面自己寫一個清理後臺的功能,比如在quick Setting中清理,可以把代碼剝離出來使用

封裝兩個方法直接使用:

  1. 獲取所有task
    返回所有後臺能清理的task
    private ArrayList<Task> 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<Task> allTasks = reloadStack();
        int count = allTasks.size();
        for(int i = 0;i < count;i++) {
           Task oneTask = allTasks.get(i);
           removeTask(oneTask);
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章