天天看點

android 6.0 SystemUI源碼分析(3)-Recent Panel加載顯示流程2.Recent Apps加載流程3.RecentActivity顯示和隐藏流程

android 6.0 SystemUI源碼分析(3)-Recent Panel加載顯示流程

轉載  http://blog.csdn.net/zhudaozhuan/article/details/50819499 1.Recent Panel按鍵處理流程 SystemUI有一個很重要的功能就是顯示近期使用的app,友善使用者點選使用。

手機長按HOME鍵或者點選Navigation Bar的近期工作列虛拟鍵可以顯示Recent Panel。

我這裡手頭上隻有Android TV平台,并且也便于debug,是以講講收到Switch按鍵後,Recent Panel的顯示流程。

KeyEvent.java中對于Switch按鍵的定義:    

    public static final int KEYCODE_APP_SWITCH      = 187;

從KeyCode的定義可以看到Recent Panel的作用是the application switcher dialog。

當KeyEvent給到WindowManagerService之前,會先給到PhoneWindowManager處理(給系統一次機會,去處理按鍵消息)。 在KeyEvent出隊列時,會走到interceptKeyBeforeDispatching函數,是以對于KEYCODE_APP_SWITCH的處理,會在這裡進行。

    @Override

    public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {

    ....     else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) {

            if (!keyguardOn) {

                if (down && repeatCount == 0) {

                    preloadRecentApps();

                } else if (!down) {

                    toggleRecentApps();

                }

            }

            return -1;

        } 在key down時,load最近使用的apps->preloadRecentApps(); 在key up時,打開或關閉Recent Panel-> toggleRecentApps();

2.Recent Apps加載流程

在PhoneWindowManager裡面開始執行preloadRecentApps()函數後,一步步調用,最終會call到我們熟悉的Recents.java,即最終是通過SystemUI去Reload Recent apps。 下面是這個函數的邏輯時序圖:

預加載Recent Apps核心函數是Recents.java中的preloadRecentsInternal函數。 函數代碼如下: [java] view plain copy

android 6.0 SystemUI源碼分析(3)-Recent Panel加載顯示流程2.Recent Apps加載流程3.RecentActivity顯示和隐藏流程
  1. void preloadRecentsInternal() {  
  2.       // Preload only the raw task list into a new load plan (which will be consumed by the  
  3.       // RecentsActivity) only if there is a task to animate to.  
  4.       ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();  
  5.       MutableBoolean topTaskHome = new MutableBoolean(true);  
  6.       RecentsTaskLoader loader = RecentsTaskLoader.getInstance();  
  7.       sInstanceLoadPlan = loader.createLoadPlan(mContext);  
  8.       if (topTask != null && !mSystemServicesProxy.isRecentsTopMost(topTask, topTaskHome)) {  
  9.           sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);  
  10.           loader.preloadTasks(sInstanceLoadPlan, topTaskHome.value);  
  11.           TaskStack top = sInstanceLoadPlan.getAllTaskStacks().get(0);  
  12.           if (top.getTaskCount() > 0) {  
  13.               preCacheThumbnailTransitionBitmapAsync(topTask, top, mDummyStackView,  
  14.                       topTaskHome.value);  
  15.           }  
  16.       }  
  17.   }  

函數主要做了如下幾件事情: 1)擷取目前運作的Task ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();

    public ActivityManager.RunningTaskInfo getTopMostTask() {

        List<ActivityManager.RunningTaskInfo> tasks = getRunningTasks(1);

        if (tasks != null && !tasks.isEmpty()) {

            return tasks.get(0);

        }

        return null;

    }

    private List<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) {

        if (mAm == null) return null;

        return mAm.getRunningTasks(numTasks); ->最終call到ActivityManager裡面的getRunningTasks

    }

2)判定目前的task是否是RecentActivity if (topTask != null && !mSystemServicesProxy.isRecentsTopMost(topTask, topTaskHome)) 

    public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,

            MutableBoolean isHomeTopMost) {

        if (topTask != null) {

            ComponentName topActivity = topTask.topActivity;

            // Check if the front most activity is recents

            if (topActivity.getPackageName().equals(Recents.sRecentsPackage) &&

                    topActivity.getClassName().equals(Recents.sRecentsActivity)) {

                if (isHomeTopMost != null) {

                    isHomeTopMost.value = false;

                }

                return true;

            }

            if (isHomeTopMost != null) {

                isHomeTopMost.value = isInHomeStack(topTask.id);

            }

        }

        return false;

    }

其中, Recents.sRecentsPackage = "com.android.systemui"

Recents.sRecentsActivity = "com.android.systemui.recents.RecentsActivity"

如果目前正在運作RecentActivity即Recent Panel正在顯示,不去執行preload recent apps行為(因為沒有意義,這個時候是去關閉Recent Panel)

3)擷取raw recent tasks sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);

這裡擷取的隻是raw tasks資料,并不是真正在UI上顯示的Tasks。

相關實作: RecentsTaskLoadPlan.java [java] view plain copy

android 6.0 SystemUI源碼分析(3)-Recent Panel加載顯示流程2.Recent Apps加載流程3.RecentActivity顯示和隐藏流程
  1.    public synchronized void preloadRawTasks(boolean isTopTaskHome) {  
  2.        mRawTasks = mSystemServicesProxy.getRecentTasks(mConfig.maxNumTasksToLoad,  
  3.                UserHandle.CURRENT.getIdentifier(), isTopTaskHome);  
  4.        Collections.reverse(mRawTasks);  
  5.        if (DEBUG) Log.d(TAG, "preloadRawTasks, tasks: " + mRawTasks.size());  
  6.    }  

調用SystemServicesProxy裡的getRecentTasks函數去RecentTasks。 mConfig.maxNumTasksToLoad在lowRamDevice是50,其他為100(lowRamDevice是指記憶體等于或低于512M低端機)

RecentsConfiguration.java  // Loading

maxNumTasksToLoad = ActivityManager.getMaxRecentTasksStatic();

ActivityManager.java

    static public int getMaxRecentTasksStatic() {

        if (gMaxRecentTasks < 0) {

            return gMaxRecentTasks = isLowRamDeviceStatic() ? 50 : 100;

        }

        return gMaxRecentTasks;

    } 擷取到的Recent Tasks存放在mRamTasks全局變量。

SystemServicesProxy.java中擷取目前的Recent Tasks,主要實作:

    public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,

            boolean isTopTaskHome) {

        if (mAm == null) return null;

       ...       List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery,

                ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |

                ActivityManager.RECENT_IGNORE_UNAVAILABLE |

                ActivityManager.RECENT_INCLUDE_PROFILES |

                ActivityManager.RECENT_WITH_EXCLUDED, userId);

        // Break early if we can't get a valid set of tasks

        if (tasks == null) {

            return new ArrayList<>();

        }

4)raw recent tasks轉化為顯示的recent Tasks 第三步擷取的raw recent tasks僅僅是原始資料,需要配合UI顯示出來。 loader.preloadTasks(sInstanceLoadPlan, topTaskHome.value);

RecentsTaskLoadPlan.java  

    synchronized void preloadPlan(RecentsTaskLoader loader, boolean isTopTaskHome) {

   ....  int taskCount = mRawTasks.size();

        for (int i = 0; i < taskCount; i++) {

            ActivityManager.RecentTaskInfo t = mRawTasks.get(i);

            // Compose the task key

            Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,

                    t.userId, t.firstActiveTime, t.lastActiveTime);

            // Get an existing activity info handle if possible

            Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();

            ActivityInfoHandle infoHandle;

            boolean hadCachedActivityInfo = false;

            if (mActivityInfoCache.containsKey(cnKey)) {

                infoHandle = mActivityInfoCache.get(cnKey);

                hadCachedActivityInfo = true;

            } else {

                infoHandle = new ActivityInfoHandle();

            }

            // Load the label, icon, and color

            String activityLabel = loader.getAndUpdateActivityLabel(taskKey, t.taskDescription,

                    mSystemServicesProxy, infoHandle);

            String contentDescription = loader.getAndUpdateContentDescription(taskKey,

                    activityLabel, mSystemServicesProxy, res);

            Drawable activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription,

                    mSystemServicesProxy, res, infoHandle, false);

            int activityColor = loader.getActivityPrimaryColor(t.taskDescription, mConfig);

            // Update the activity info cache

            if (!hadCachedActivityInfo && infoHandle.info != null) {

                mActivityInfoCache.put(cnKey, infoHandle);

            }

            Bitmap icon = t.taskDescription != null

                    ? t.taskDescription.getInMemoryIcon()

                    : null;

            String iconFilename = t.taskDescription != null

                    ? t.taskDescription.getIconFilename()

                    : null;

            // Add the task to the stack

            Task task = new Task(taskKey, (t.id != RecentsTaskLoader.INVALID_TASK_ID),

                    t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel, contentDescription,

                    activityIcon, activityColor, (i == (taskCount - 1)), mConfig.lockToAppEnabled,

                    icon, iconFilename);

            task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy, false);

            if (DEBUG) Log.d(TAG, "\tthumbnail: " + taskKey + ", " + task.thumbnail);

            if (!mConfig.multiStackEnabled ||

                    Constants.DebugFlags.App.EnableMultiStackToSingleStack) {

                int firstStackId = 0;

                ArrayList<Task> stackTasks = stacksTasks.get(firstStackId);

                if (stackTasks == null) {

                    stackTasks = new ArrayList<>();

                    stacksTasks.put(firstStackId, stackTasks);

                }

                stackTasks.add(task);

            } else {

                ArrayList<Task> stackTasks = stacksTasks.get(t.stackId);

                if (stackTasks == null) {

                    stackTasks = new ArrayList<>();

                    stacksTasks.put(t.stackId, stackTasks);

                }

                stackTasks.add(task);

            }

        }

擷取RawTasks後,進行各種初始化,初始化的目的是為了配合UI顯示。

Recent Tasks Preload Follow時序圖如下:

3.RecentActivity顯示和隐藏流程

在第一部分已經講到,PhoneWindowManager在接收到KEYCODE_APP_SWITCH KeyEvent後,在key up時會進行開關Recent Panel顯示的邏輯處理。

整理為時序圖如下:

即最終會call到SystemUI的Recents.java中的toggleRecents()。

下面分析一下這個函數的處理邏輯(注意在第二部分我們分析到Raw Recent Tasks的資料,怎麼給到RecentActivity使用的) 1)RecentActivity顯示或消失判定依據 RecentActivity.java [java] view plain copy

android 6.0 SystemUI源碼分析(3)-Recent Panel加載顯示流程2.Recent Apps加載流程3.RecentActivity顯示和隐藏流程
  1.  void toggleRecentsActivity() {  
  2.      // If the user has toggled it too quickly, then just eat up the event here (it's better than  
  3.      // showing a janky screenshot).  
  4.      // NOTE: Ideally, the screenshot mechanism would take the window transform into account  
  5.      if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) {  
  6.          return;  
  7.      }  
  8.      // If Recents is the front most activity, then we should just communicate with it directly  
  9.      // to launch the first task or dismiss itself  
  10.      ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();  
  11.      MutableBoolean isTopTaskHome = new MutableBoolean(true);  
  12.      if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {  
  13.          // Notify recents to toggle itself  
  14.          Intent intent = createLocalBroadcastIntent(mContext, ACTION_TOGGLE_RECENTS_ACTIVITY);  
  15.          mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);  
  16.          mLastToggleTime = SystemClock.elapsedRealtime();  
  17.          return;  
  18.      } else {  
  19.          // Otherwise, start the recents activity  
  20.          startRecentsActivity(topTask, isTopTaskHome.value);  
  21.      }  
  22.  }  

RecentActivity消失的條件:目前Task的Activity為RecentActivity,則表示目前已經是RecentActivity,那麼不重新整理資料,直接消失。 ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask(); topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)  ->這段code第二部分有分析過實作過程 // Notify recents to toggle itself

Intent intent = createLocalBroadcastIntent(mContext, ACTION_TOGGLE_RECENTS_ACTIVITY);

mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);

mLastToggleTime = SystemClock.elapsedRealtime();

->這裡會發送一個廣播,通知RecentActivity

RecentActivity.java 注冊上面提到的廣播: // Register the broadcast receiver to handle messages from our service

IntentFilter filter = new IntentFilter();

filter.addAction(Recents.ACTION_HIDE_RECENTS_ACTIVITY);

 filter.addAction(Recents.ACTION_TOGGLE_RECENTS_ACTIVITY);

 filter.addAction(Recents.ACTION_START_ENTER_ANIMATION);

registerReceiver(mServiceBroadcastReceiver, filter);

廣播接收器mServiceBroadcastReceiver處理邏輯: else if (action.equals(Recents.ACTION_TOGGLE_RECENTS_ACTIVITY)) {

// If we are toggling Recents, then first unfilter any filtered stacks first

dismissRecentsToFocusedTaskOrHome(true);

如果目前不是RecentActivity,那麼走顯示RecentActivity邏輯。 // Otherwise, start the recents activity

startRecentsActivity(topTask, isTopTaskHome.value);

2)啟動RecentActivity startRecentsActivity函數會根據兩種不同的source,傳入不同的參數,這裡說的source隻是從Home和非Home啟動RecentActivity 從Home啟動:  // Determine whether we are coming from a search owned home activity

boolean fromSearchHome = (homeActivityPackage != null) &&

                        homeActivityPackage.equals(searchWidgetPackage);

ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);

startAlternateRecentsActivity(topTask, opts, true , fromSearchHome, false , stackVr); 從非Home啟動:  // Otherwise we do the normal fade from an unknown source

ActivityOptions opts = getUnknownTransitionActivityOptions();

startAlternateRecentsActivity(topTask, opts, true ,false , false , stackVr);

最後在startAlternateRecentsActivity函數中啟動RecentActivity:  Intent intent = new Intent(sToggleRecentsAction);

intent.setClassName(sRecentsPackage, sRecentsActivity);

 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK

                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);

if (opts != null) {

      mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);

} else {

     mContext.startActivityAsUser(intent, UserHandle.CURRENT);

}

3)在RecentsActivity 中重新整理Recent Tasks 在RecentsActivity的onStart函數中,會call updateRecentsTasks()函數重新整理Tasks資料。 RecentsActivity.java

[java] view plain copy

android 6.0 SystemUI源碼分析(3)-Recent Panel加載顯示流程2.Recent Apps加載流程3.RecentActivity顯示和隐藏流程
  1. void updateRecentsTasks() {  
  2.     // If AlternateRecentsComponent has preloaded a load plan, then use that to prevent  
  3.     // reconstructing the task stack  
  4.     RecentsTaskLoader loader = RecentsTaskLoader.getInstance();  
  5.     RecentsTaskLoadPlan plan = Recents.consumeInstanceLoadPlan();  
  6.     if (plan == null) {  
  7.         plan = loader.createLoadPlan(this);  
  8.     }  
  9.     // Start loading tasks according to the load plan  
  10.     if (!plan.hasTasks()) {  
  11.         loader.preloadTasks(plan, mConfig.launchedFromHome);  
  12.     }  
  13.     RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();  
  14.     loadOpts.runningTaskId = mConfig.launchedToTaskId;  
  15.     loadOpts.numVisibleTasks = mConfig.launchedNumVisibleTasks;  
  16.     loadOpts.numVisibleTaskThumbnails = mConfig.launchedNumVisibleThumbnails;  
  17.     loader.loadTasks(this, plan, loadOpts);  
  18.     ArrayList<TaskStack> stacks = plan.getAllTaskStacks();  
  19.     mConfig.launchedWithNoRecentTasks = !plan.hasTasks();  
  20.     if (!mConfig.launchedWithNoRecentTasks) {  
  21.         mRecentsView.setTaskStacks(stacks);  
  22.     }  

mRecentsView設定的stacks就是我們在第二部擷取的資料。

RecentsTaskLoadPlan.java [java] view plain copy

android 6.0 SystemUI源碼分析(3)-Recent Panel加載顯示流程2.Recent Apps加載流程3.RecentActivity顯示和隐藏流程
  1. public ArrayList<TaskStack> getAllTaskStacks() {  
  2.     ArrayList<TaskStack> stacks = new ArrayList<TaskStack>();  
  3.     int stackCount = mStacks.size();  
  4.     for (int i = 0; i < stackCount; i++) {  
  5.         stacks.add(mStacks.valueAt(i));  
  6.     }  
  7.     // Ensure that we have at least one stack  
  8.     if (stacks.isEmpty()) {  
  9.         stacks.add(new TaskStack());  
  10.     }  
  11.     return stacks;  
  12. }  

這裡的mStacks即我們第二步封裝的資料。

繼續閱讀