android 6.0 SystemUI源码分析(3)-Recent Panel加载显示流程


1.Recent Panel按键处理流程
SystemUI有一个很重要的功能就是显示近期使用的app,方便用户点击使用。

手机长按HOME键或者点击Navigation Bar的近期任务栏虚拟键可以显示Recent Panel。

我这里手头上只有Android TV平台,并且也便于debug,所以讲讲收到Switch按键后,Recent Panel的显示流程。

KeyEvent.java中对于Switch按键的定义:
    /** Key code constant: App switch key.
     * Should bring up the application switcher dialog. */
    public static final int KEYCODE_APP_SWITCH      = 187;

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

当KeyEvent给到WindowManagerService之前,会先给到PhoneWindowManager处理(给系统一次机会,去处理按键消息)。
在KeyEvent出队列时,会走到interceptKeyBeforeDispatching函数,因此对于KEYCODE_APP_SWITCH的处理,会在这里进行。

 /** {@inheritDoc} */
    @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函数。
函数代码如下:
  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();

 /** Returns the top task. */
    public ActivityManager.RunningTaskInfo getTopMostTask() {
        List<ActivityManager.RunningTaskInfo> tasks = getRunningTasks(1);
        if (tasks != null && !tasks.isEmpty()) {
            return tasks.get(0);
        }
        return null;
    }

  /** Returns a list of the running tasks */
    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)) 

/** Returns whether the recents is currently running */
    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
  1. /** 
  2.     * An optimization to preload the raw list of tasks. 
  3.     */  
  4.    public synchronized void preloadRawTasks(boolean isTopTaskHome) {  
  5.        mRawTasks = mSystemServicesProxy.getRecentTasks(mConfig.maxNumTasksToLoad,  
  6.                UserHandle.CURRENT.getIdentifier(), isTopTaskHome);  
  7.        Collections.reverse(mRawTasks);  
  8.   
  9.        if (DEBUG) Log.d(TAG, "preloadRawTasks, tasks: " + mRawTasks.size());  
  10.    }  
调用SystemServicesProxy里的getRecentTasks函数去RecentTasks。
mConfig.maxNumTasksToLoad在lowRamDevice是50,其他为100(lowRamDevice是指内存等于或低于512M低端机)

RecentsConfiguration.java
 // Loading
maxNumTasksToLoad = ActivityManager.getMaxRecentTasksStatic();

ActivityManager.java
/**
     * Return the maximum number of recents entries that we will maintain and show.
     * @hide
     */
    static public int getMaxRecentTasksStatic() {
        if (gMaxRecentTasks < 0) {
            return gMaxRecentTasks = isLowRamDeviceStatic() ? 50 : 100;
        }
        return gMaxRecentTasks;
    }
获取到的Recent Tasks存放在mRamTasks全局变量。

SystemServicesProxy.java中获取当前的Recent Tasks,主要实现:
/** Returns a list of the recents 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
 /**
     * Preloads the list of recent tasks from the system.  After this call, the TaskStack will
     * have a list of all the recent tasks with their metadata, not including icons or
     * thumbnails which were not cached and have to be loaded.
     */
    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
  1. /** Toggles the recents activity */  
  2.  void toggleRecentsActivity() {  
  3.      // If the user has toggled it too quickly, then just eat up the event here (it's better than  
  4.      // showing a janky screenshot).  
  5.      // NOTE: Ideally, the screenshot mechanism would take the window transform into account  
  6.      if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) {  
  7.          return;  
  8.      }  
  9.   
  10.      // If Recents is the front most activity, then we should just communicate with it directly  
  11.      // to launch the first task or dismiss itself  
  12.      ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();  
  13.      MutableBoolean isTopTaskHome = new MutableBoolean(true);  
  14.      if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {  
  15.          // Notify recents to toggle itself  
  16.          Intent intent = createLocalBroadcastIntent(mContext, ACTION_TOGGLE_RECENTS_ACTIVITY);  
  17.          mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);  
  18.          mLastToggleTime = SystemClock.elapsedRealtime();  
  19.          return;  
  20.      } else {  
  21.          // Otherwise, start the recents activity  
  22.          startRecentsActivity(topTask, isTopTaskHome.value);  
  23.      }  
  24.  }  
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 /* fromHome */, fromSearchHome, false /* fromThumbnail */, stackVr);
从非Home启动:
 // Otherwise we do the normal fade from an unknown source
ActivityOptions opts = getUnknownTransitionActivityOptions();
startAlternateRecentsActivity(topTask, opts, true /* fromHome */,false /* fromSearchHome */, false /* fromThumbnail */, 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

  1. /** Updates the set of recent tasks */  
  2. void updateRecentsTasks() {  
  3.     // If AlternateRecentsComponent has preloaded a load plan, then use that to prevent  
  4.     // reconstructing the task stack  
  5.     RecentsTaskLoader loader = RecentsTaskLoader.getInstance();  
  6.     RecentsTaskLoadPlan plan = Recents.consumeInstanceLoadPlan();  
  7.     if (plan == null) {  
  8.         plan = loader.createLoadPlan(this);  
  9.     }  
  10.   
  11.     // Start loading tasks according to the load plan  
  12.     if (!plan.hasTasks()) {  
  13.         loader.preloadTasks(plan, mConfig.launchedFromHome);  
  14.     }  
  15.     RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();  
  16.     loadOpts.runningTaskId = mConfig.launchedToTaskId;  
  17.     loadOpts.numVisibleTasks = mConfig.launchedNumVisibleTasks;  
  18.     loadOpts.numVisibleTaskThumbnails = mConfig.launchedNumVisibleThumbnails;  
  19.     loader.loadTasks(this, plan, loadOpts);  
  20.   
  21.     ArrayList<TaskStack> stacks = plan.getAllTaskStacks();  
  22.     mConfig.launchedWithNoRecentTasks = !plan.hasTasks();  
  23.     if (!mConfig.launchedWithNoRecentTasks) {  
  24.         mRecentsView.setTaskStacks(stacks);  
  25.     }  
mRecentsView设置的stacks就是我们在第二部获取的数据。

RecentsTaskLoadPlan.java
  1. /** 
  2.  * Returns all TaskStacks from the preloaded list of recent tasks. 
  3.  */  
  4. public ArrayList<TaskStack> getAllTaskStacks() {  
  5.     ArrayList<TaskStack> stacks = new ArrayList<TaskStack>();  
  6.     int stackCount = mStacks.size();  
  7.     for (int i = 0; i < stackCount; i++) {  
  8.         stacks.add(mStacks.valueAt(i));  
  9.     }  
  10.     // Ensure that we have at least one stack  
  11.     if (stacks.isEmpty()) {  
  12.         stacks.add(new TaskStack());  
  13.     }  
  14.     return stacks;  
  15. }  
这里的mStacks即我们第二步封装的数据。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值