Android SystemUI之Recent,近期列表(五)

Android  SystemUI系列:

     1.Android  SystemUI之启动流程(一)

     2.Android SystemUI之StatusBar,状态栏(二)

     3.Android SystemUI之下拉菜单,通知栏,快捷面板(三)

     4.Android SystemUI之NavigationBar,导航栏(四)

     5.Android SystemUI之Recent,近期列表(五)

 

一、近期任务列表启动

       1.近期列表View结构图

   

 

2.NavigationBarFragment.onRecentsClick和NavigationBarFragment.onRecentsTouch

结合上个博客导航栏,我们可以知道近期列表的启动入口是onRecentsTouch和onRecentsClick

onRecentsTouch 

 private boolean onRecentsTouch(View v, MotionEvent event) {
        int action = event.getAction() & MotionEvent.ACTION_MASK;
        if (action == MotionEvent.ACTION_DOWN) {
            mCommandQueue.preloadRecentApps();//启动近期列表
        } else if (action == MotionEvent.ACTION_CANCEL) {
            mCommandQueue.cancelPreloadRecentApps();
        } else if (action == MotionEvent.ACTION_UP) {
            if (!v.isPressed()) {
                mCommandQueue.cancelPreloadRecentApps();
            }
        }
        return false;
    }
 public void preloadRecentApps() {
        synchronized (mLock) {
            mHandler.removeMessages(MSG_PRELOAD_RECENT_APPS);
            mHandler.obtainMessage(MSG_PRELOAD_RECENT_APPS, 0, 0, null).sendToTarget();
        }
    }

 

 case MSG_PRELOAD_RECENT_APPS:
                    for (int i = 0; i < mCallbacks.size(); i++) {
                        mCallbacks.get(i).preloadRecentApps();
                    }

 onRecentsClick

 private void onRecentsClick(View v) {
        if (LatencyTracker.isEnabled(getContext())) {
            LatencyTracker.getInstance(getContext()).onActionStart(
                    LatencyTracker.ACTION_TOGGLE_RECENTS);
        }
        mStatusBar.awakenDreams();
        mCommandQueue.toggleRecentApps();
    }
 public void toggleRecentApps() {
        synchronized (mLock) {
            mHandler.removeMessages(MSG_TOGGLE_RECENT_APPS);
            Message msg = mHandler.obtainMessage(MSG_TOGGLE_RECENT_APPS, 0, 0, null);
            msg.setAsynchronous(true);
            msg.sendToTarget();
        }
    }
 case MSG_TOGGLE_RECENT_APPS:
                    for (int i = 0; i < mCallbacks.size(); i++) {
                        mCallbacks.get(i).toggleRecentApps();
                    }

Recents这个类继承CommandQueue.Callbacks,所以上述函数

mCallbacks.get(i).preloadRecentApps()会调用Recents.preloadRecentApps

mCallbacks.get(i).toggleRecentApps()会调用Recents.toggleRecentApps

Recents.preloadRecentApps

 public void preloadRecentApps() {
        // Ensure the device has been provisioned before allowing the user to interact with
        // recents

        if (!isUserSetup()) {
            return;
        }
		

        if (mOverviewProxyService.getProxy() != null) {
            // TODO: Proxy to Launcher
            return;
        }

        int currentUser = sSystemServicesProxy.getCurrentUser();
	
        if (sSystemServicesProxy.isSystemUser(currentUser)) {
			
            mImpl.preloadRecents();
        } else {
            if (mSystemToUserCallbacks != null) {
                IRecentsNonSystemUserCallbacks callbacks =
                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
                if (callbacks != null) {
                    try {
 						
                        callbacks.preloadRecents();
                    } catch (RemoteException e) {
                        Log.e(TAG, "Callback failed", e);
                    }
                } else {
                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
                }
            }
        }
    }

 if (mOverviewProxyService.getProxy() != null) {
            // TODO: Proxy to Launcher
            return;
        }

mOverviewProxyService.getProxy() 是什么时候不为空呢?远程服务IOverviewProxy存在的时候,那么这个远程服务在哪里实现呢?

OverviewProxyService里面有这段代码: 

mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
                com.android.internal.R.string.config_recentsComponentName));

<string name="config_recentsComponentName" translatable="false">com.android.launcher3/com.android.quickstep.RecentsActivity</string>

所以在9.0以后你会发现近期列表的实现在Launcher3里面,不过本博文还是研究systemui的近期列表。com.android.systemui/.recents.RecentsActivity

 

RecentsImpl.preloadRecents

public void preloadRecents() {
    //ScreenPin正在使用就停止
        if (ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
            return;
        }

        // Skip preloading recents when keyguard is showing
        //锁屏也停止
        final StatusBar statusBar = getStatusBar();
        if (statusBar != null && statusBar.isKeyguardShowing()) {
            return;
        }

        // Preload only the raw task list into a new load plan (which will be consumed by the
        // RecentsActivity) only if there is a task to animate to.  Post this to ensure that we
        // don't block the touch feedback on the nav bar button which triggers this.
        mHandler.post(() -> {
            SystemServicesProxy ssp = Recents.getSystemServices();
            if (!ssp.isRecentsActivityVisible(null)) {
				//从ams上面获取运行的任务栈的信息
                ActivityManager.RunningTaskInfo runningTask =
                        ActivityManagerWrapper.getInstance().getRunningTask();
                if (runningTask == null) {
                    return;
                }

                RecentsTaskLoader loader = Recents.getTaskLoader();
                sInstanceLoadPlan = new RecentsTaskLoadPlan(mContext);
                loader.preloadTasks(sInstanceLoadPlan, runningTask.id);
                TaskStack stack = sInstanceLoadPlan.getTaskStack();
                if (stack.getTaskCount() > 0) {
                    // Only preload the icon (but not the thumbnail since it may not have been taken
                    // for the pausing activity)
                    preloadIcon(runningTask.id);

                    // At this point, we don't know anything about the stack state.  So only
                    // calculate the dimensions of the thumbnail that we need for the transition
                    // into Recents, but do not draw it until we construct the activity options when
                    // we start Recents
                    updateHeaderBarLayout(stack, null /* window rect override*/);
                }
            }
        });
    }

  上述代码涉及到RecentsTaskLoader,RecentsTaskLoadPlan,TaskStack,这三个类会伴随近期列表的创建和消亡,可见其重要。

 RecentsTaskLoader和RecentsTaskLoadPlan主要加载最近任务相关数据,比如图标,名称,截图等

TaskStack这个管理所有信息加载完后保持在Task的栈。

loader.preloadTasks这个会加载对应的资源文件

 public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId) {
        preloadTasks(plan, runningTaskId, ActivityManagerWrapper.getInstance().getCurrentUserId());
    }

    /** Preloads recents tasks using the specified plan to store the output. */
    public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId,
            int currentUserId) {
        try {
            Trace.beginSection("preloadPlan");
            plan.preloadPlan(new PreloadOptions(), this, runningTaskId, currentUserId);
        } finally {
            Trace.endSection();
        }
    }
  public void preloadPlan(PreloadOptions opts, RecentsTaskLoader loader, int runningTaskId,
            int currentUserId) {
        Resources res = mContext.getResources();
        ArrayList<Task> allTasks = new ArrayList<>();
		//获取近期列表task
        if (mRawTasks == null) {
            mRawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(
                    ActivityManager.getMaxRecentTasksStatic(), currentUserId);

            // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
            Collections.reverse(mRawTasks);
        }

        int taskCount = mRawTasks.size();
		
        for (int i = 0; i < taskCount; i++) {
            ActivityManager.RecentTaskInfo t = mRawTasks.get(i);

            // Compose the task key
            final ComponentName sourceComponent = t.origActivity != null
                    // Activity alias if there is one
                    ? t.origActivity
                    // The real activity if there is no alias (or the target if there is one)
                    : t.realActivity;
            final int windowingMode = t.configuration.windowConfiguration.getWindowingMode();
            TaskKey taskKey = new TaskKey(t.persistentId, windowingMode, t.baseIntent,
                    sourceComponent, t.userId, t.lastActiveTime);

            boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM;
            boolean isStackTask = !isFreeformTask;
            boolean isLaunchTarget = taskKey.id == runningTaskId;

            ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
            if (info == null) {
                continue;
            }

            // Load the title, icon, and color
            //taskDescription 这个类里面保持了mLabel,Bitmap mIcon等信息,getAndUpdateActivityTitle返回label信息
            String title = opts.loadTitles
                    ? loader.getAndUpdateActivityTitle(taskKey, t.taskDescription)
                    : "";
            String titleDescription = opts.loadTitles
                    ? loader.getAndUpdateContentDescription(taskKey, t.taskDescription)
                    : "";
            Drawable icon = isStackTask
                    ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, false)
                    : null;
            ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey,
                    false /* loadIfNotCached */, false /* storeInCache */);
	
            int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
            int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
            boolean isSystemApp = (info != null) &&
                    ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);

            // TODO: Refactor to not do this every preload
            if (mTmpLockedUsers.indexOfKey(t.userId) < 0) {
                mTmpLockedUsers.put(t.userId, mKeyguardManager.isDeviceLocked(t.userId));
            }
            boolean isLocked = mTmpLockedUsers.get(t.userId);

            // Add the task to the stack
            Task task = new Task(taskKey, icon,
                    thumbnail, title, titleDescription, activityColor, backgroundColor,
                    isLaunchTarget, isStackTask, isSystemApp, t.supportsSplitScreenMultiWindow,
                    t.taskDescription, t.resizeMode, t.topActivity, isLocked);

            allTasks.add(task);
        }

        // Initialize the stacks
        mStack = new TaskStack();
        mStack.setTasks(allTasks, false /* notifyStackChanges */);
    }

 

 public void setTasks(List<Task> tasks, boolean notifyStackChanges) {
        // Compute a has set for each of the tasks
       
        ArrayMap<TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
        ArrayMap<TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
        ArrayList<Task> addedTasks = new ArrayList<>();
        ArrayList<Task> removedTasks = new ArrayList<>();
        ArrayList<Task> allTasks = new ArrayList<>();
	Log.i("Systemui-xiao","TaskStack  setTasks tasks:"+tasks+",mRawTaskList:"+mRawTaskList);	

        // Disable notifications if there are no callbacks
        if (mCb == null) {
            notifyStackChanges = false;
        }

        // Remove any tasks that no longer exist
        int taskCount = mRawTaskList.size();
        for (int i = taskCount - 1; i >= 0; i--) {
            Task task = mRawTaskList.get(i);
            if (!newTasksMap.containsKey(task.key)) {
                if (notifyStackChanges) {
                    removedTasks.add(task);
                }
            }
        }

        // Add any new tasks
        taskCount = tasks.size();
        for (int i = 0; i < taskCount; i++) {
            Task newTask = tasks.get(i);
            Task currentTask = currentTasksMap.get(newTask.key);
            if (currentTask == null && notifyStackChanges) {
                addedTasks.add(newTask);
            } else if (currentTask != null) {
                // The current task has bound callbacks, so just copy the data from the new task
                // state and add it back into the list
                currentTask.copyFrom(newTask);
                newTask = currentTask;
            }
            allTasks.add(newTask);
        }

        // Sort all the tasks to ensure they are ordered correctly
        for (int i = allTasks.size() - 1; i >= 0; i--) {
            allTasks.get(i).temporarySortIndexInStack = i;
        }

        mStackTaskList.set(allTasks);
        mRawTaskList.clear();
        mRawTaskList.addAll(allTasks);

        // Only callback for the removed tasks after the stack has updated
        int removedTaskCount = removedTasks.size();
        Task newFrontMostTask = getFrontMostTask();
        for (int i = 0; i < removedTaskCount; i++) {
            mCb.onStackTaskRemoved(this, removedTasks.get(i), newFrontMostTask,
                    AnimationProps.IMMEDIATE, false /* fromDockGesture */,
                    true /* dismissRecentsIfAllRemoved */);
        }

        // Only callback for the newly added tasks after this stack has been updated
        int addedTaskCount = addedTasks.size();
        for (int i = 0; i < addedTaskCount; i++) {
            mCb.onStackTaskAdded(this, addedTasks.get(i));
        }

        // Notify that the task stack has been updated
        if (notifyStackChanges) {
            mCb.onStackTasksUpdated(this);
        }
    }

 mCb.onStackTaskAdded(this, addedTasks.get(i));

    mCb.onStackTasksUpdated(this);

会回调TaskStackView里面的onStackTaskAdded和onStackTasksUpdated  。由于现在TaskStackView还没加载起来,所以现在是暂时不会调用,不过后续等TaskStackView加载起来,再调用setTasks就能起到数据变化,近期任务列表也会相应的添加对应的近期任务。

preloadPlan这个函数主要就是加载资源文件。

 

Recents.toggleRecentApps

    public void toggleRecentApps() {
        // Ensure the device has been provisioned before allowing the user to interact with
        // recents

        if (!isUserSetup()) {
            return;
        }

        // If connected to launcher service, let it handle the toggle logic
        IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
		
        if (overviewProxy != null) {
            final Runnable toggleRecents = () -> {
                try {
                    if (mOverviewProxyService.getProxy() != null) {
                        mOverviewProxyService.getProxy().onOverviewToggle();
                    }
                } catch (RemoteException e) {
                    Log.e(TAG, "Cannot send toggle recents through proxy service.", e);
                }
            };
            // Preload only if device for current user is unlocked
            final StatusBar statusBar = getComponent(StatusBar.class);

			
            if (statusBar != null && statusBar.isKeyguardShowing()) {
                statusBar.executeRunnableDismissingKeyguard(() -> {
                        // Flush trustmanager before checking device locked per user
                        mTrustManager.reportKeyguardShowingChanged();
			 				
                        mHandler.post(toggleRecents);
                    }, null,  true /* dismissShade */, false /* afterKeyguardGone */,
                    true /* deferred */);
            } else {
				
                toggleRecents.run();
            }
            return;
        }

        int growTarget = getComponent(Divider.class).getView().growsRecents();
        int currentUser = sSystemServicesProxy.getCurrentUser();
		
       
            mImpl.toggleRecents(growTarget);
        } else {
            if (mSystemToUserCallbacks != null) {
                IRecentsNonSystemUserCallbacks callbacks =
                        mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
                if (callbacks != null) {
                    try {
						
                        callbacks.toggleRecents(growTarget);
                    } catch (RemoteException e) {
                        Log.e(TAG, "Callback failed", e);
                    }
                } else {
                    Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
                }
            }
        }
    }

RecentsImpl.toggleRecents

 public void toggleRecents(int growTarget) {
       //判断ScreenPinn是否允许中,如果运行中就不启动近期任务列表
        if (ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
            return;
        }

        // Skip this toggle if we are already waiting to trigger recents via alt-tab
        if (mFastAltTabTrigger.isDozing()) {
            return;
        }

        if (mWaitingForTransitionStart) {
            mToggleFollowingTransitionStart = true;
            return;
        }

        mDraggingInRecents = false;
        mLaunchedWhileDocking = false;
        mTriggeredFromAltTab = false;

        try {
            MutableBoolean isHomeStackVisible = new MutableBoolean(true);
            long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime;

            SystemServicesProxy ssp = Recents.getSystemServices();
			//ssp.isRecentsActivityVisible判断近期列表是否已经启动,如果启动的话就再启动它
            if (ssp.isRecentsActivityVisible(isHomeStackVisible)) {
                RecentsConfiguration config = Recents.getConfiguration();
                RecentsActivityLaunchState launchState = config.getLaunchState();
                if (!launchState.launchedWithAltTab) {
                    if (Recents.getConfiguration().isGridEnabled) {
                        // Has the user tapped quickly?
                        boolean isQuickTap = elapsedTime < ViewConfiguration.getDoubleTapTimeout();
                        if (isQuickTap) {
                            EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
                        } else {
                            EventBus.getDefault().post(new LaunchMostRecentTaskRequestEvent());
                        }
                    } else {
                        // Launch the next focused task
                        EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
                    }
                } else {
                    // If the user has toggled it too quickly, then just eat up the event here (it's
                    // better than showing a janky screenshot).
                    // NOTE: Ideally, the screenshot mechanism would take the window transform into
                    // account
                    if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
                        return;
                    }

                    EventBus.getDefault().post(new ToggleRecentsEvent());
                    mLastToggleTime = SystemClock.elapsedRealtime();
                }
                return;
            } else {
                // If the user has toggled it too quickly, then just eat up the event here (it's
                // better than showing a janky screenshot).
                // NOTE: Ideally, the screenshot mechanism would take the window transform into
                // account
                if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
                    return;
                }

                // Otherwise, start the recents activity
                ActivityManager.RunningTaskInfo runningTask =
                        ActivityManagerWrapper.getInstance().getRunningTask();
                startRecentsActivityAndDismissKeyguardIfNeeded(runningTask,
                        isHomeStackVisible.value, true /* animate */, growTarget);

                // Only close the other system windows if we are actually showing recents
                ActivityManagerWrapper.getInstance().closeSystemWindows(
                        SYSTEM_DIALOG_REASON_RECENT_APPS);
                mLastToggleTime = SystemClock.elapsedRealtime();
            }
        } catch (ActivityNotFoundException e) {
            Log.e(TAG, "Failed to launch RecentsActivity", e);
        }
    }
 protected void startRecentsActivityAndDismissKeyguardIfNeeded(
            final ActivityManager.RunningTaskInfo runningTask, final boolean isHomeStackVisible,
            final boolean animate, final int growTarget) {
        // Preload only if device for current user is unlocked
        final StatusBar statusBar = getStatusBar();
		//如果是锁屏界面就延迟启动近期列表,否则就直接启动
        if (statusBar != null && statusBar.isKeyguardShowing()) {
            statusBar.executeRunnableDismissingKeyguard(() -> {
                    // Flush trustmanager before checking device locked per user when preloading
                    mTrustManager.reportKeyguardShowingChanged();
                    mHandler.post(() -> startRecentsActivity(runningTask, isHomeStackVisible,
                            animate, growTarget));
                }, null,  true /* dismissShade */, false /* afterKeyguardGone */,
                true /* deferred */);
        } else {
            startRecentsActivity(runningTask, isHomeStackVisible, animate, growTarget);
        }
    }
  private void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
            boolean isHomeStackVisible, boolean animate, int growTarget) {
             
        RecentsTaskLoader loader = Recents.getTaskLoader();
        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();

        int runningTaskId = !mLaunchedWhileDocking && (runningTask != null)
                ? runningTask.id
                : -1;

        // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
        // should always preload the tasks now. If we are dragging in recents, reload them as
        // the stacks might have changed.
        if (mLaunchedWhileDocking || mTriggeredFromAltTab || sInstanceLoadPlan == null) {
            // Create a new load plan if preloadRecents() was never triggered
            sInstanceLoadPlan = new RecentsTaskLoadPlan(mContext);
        }
        if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
            loader.preloadTasks(sInstanceLoadPlan, runningTaskId);//预加载近期任务栈的资源文件,比如截屏图片,标题等
        }

	

        TaskStack stack = sInstanceLoadPlan.getTaskStack();
        boolean hasRecentTasks = stack.getTaskCount() > 0;
        boolean useThumbnailTransition = (runningTask != null) && !isHomeStackVisible &&
                hasRecentTasks;

        // Update the launch state that we need in updateHeaderBarLayout()
        launchState.launchedFromHome = !useThumbnailTransition && !mLaunchedWhileDocking;
        launchState.launchedFromApp = useThumbnailTransition || mLaunchedWhileDocking;
        launchState.launchedFromPipApp = false;
        launchState.launchedWithNextPipApp =
                stack.isNextLaunchTargetPip(RecentsImpl.getLastPipTime());
        launchState.launchedViaDockGesture = mLaunchedWhileDocking;
        launchState.launchedViaDragGesture = mDraggingInRecents;
        launchState.launchedToTaskId = runningTaskId;
        launchState.launchedWithAltTab = mTriggeredFromAltTab;

        // Disable toggling of recents between starting the activity and it is visible and the app
        // has started its transition into recents.
        setWaitingForTransitionStart(useThumbnailTransition);

        // Preload the icon (this will be a null-op if we have preloaded the icon already in
        // preloadRecents())
    if(StatusBar.SYSTEMUI_RENCENT_DEBUG)Log.i(StatusBar.TAG_XIAO,"RecentsImpl startRecentsActivity1 preloadIcon runningTaskId:"+runningTaskId);      
        preloadIcon(runningTaskId);//预加载icon信息

        // Update the header bar if necessary
        Rect windowOverrideRect = getWindowRectOverride(growTarget);
        updateHeaderBarLayout(stack, windowOverrideRect);

        // Prepare the dummy stack for the transition
        TaskStackLayoutAlgorithm.VisibilityReport stackVr =
                mDummyStackView.computeStackVisibilityReport();

        // Update the remaining launch state
        launchState.launchedNumVisibleTasks = stackVr.numVisibleTasks;
        launchState.launchedNumVisibleThumbnails = stackVr.numVisibleThumbnails;

        if (!animate) {
            startRecentsActivity(ActivityOptions.makeCustomAnimation(mContext, -1, -1),
                    null /* future */);
            return;
        }

        Pair<ActivityOptions, AppTransitionAnimationSpecsFuture> pair;
        if (useThumbnailTransition) {
            // Try starting with a thumbnail transition
            pair = getThumbnailTransitionActivityOptions(runningTask, windowOverrideRect);
        } else {
            // If there is no thumbnail transition, but is launching from home into recents, then
            // use a quick home transition
            pair = new Pair<>(hasRecentTasks
                    ? getHomeTransitionActivityOptions()
                    : getUnknownTransitionActivityOptions(), null);
        }
        startRecentsActivity(pair.first, pair.second);//启动RecentsActivity
        mLastToggleTime = SystemClock.elapsedRealtime();
    }

 
  private void startRecentsActivity(ActivityOptions opts,
            final AppTransitionAnimationSpecsFuture future) {
             
        Intent intent = new Intent();
        intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
                | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
        HidePipMenuEvent hideMenuEvent = new HidePipMenuEvent();
        hideMenuEvent.addPostAnimationCallback(() -> {
            Recents.getSystemServices().startActivityAsUserAsync(intent, opts);
            EventBus.getDefault().send(new RecentsActivityStartingEvent());
            if (future != null) {
                future.composeSpecsSynchronous();
            }
        });
        EventBus.getDefault().send(hideMenuEvent);

        // Once we have launched the activity, reset the dummy stack view tasks so we don't hold
        // onto references to the same tasks consumed by the activity
   if(StatusBar.SYSTEMUI_RENCENT_DEBUG)Log.i(StatusBar.TAG_XIAO,"RecentsImpl startRecentsActivity2 setTasks");      
        mDummyStackView.setTasks(mEmptyTaskStack, false /* notifyStackChanges */);
    }

    public final static String RECENTS_PACKAGE = "com.android.systemui";
    public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";

通过一系列的准备我们找到启动RecentsActivity的入口了

 

二、近期任务列表数据加载和创建

   1.RecentsActivity

  

 public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mFinishedOnStartup = false;

        // In the case that the activity starts up before the Recents component has initialized
        // (usually when debugging/pushing the SysUI apk), just finish this activity.
        SystemServicesProxy ssp = Recents.getSystemServices();
        if (ssp == null) {
            mFinishedOnStartup = true;
            finish();
            return;
        }

        // Register this activity with the event bus
        EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);

        // Initialize the package monitor
        mPackageMonitor.register(this, Looper.getMainLooper(), UserHandle.ALL,
                true /* externalStorage */);

        // Select theme based on wallpaper colors
        mColorExtractor = Dependency.get(SysuiColorExtractor.class);
        mColorExtractor.addOnColorsChangedListener(this);
        mUsingDarkText = mColorExtractor.getColors(ColorExtractor.TYPE_DARK,
                WallpaperManager.FLAG_SYSTEM, true).supportsDarkText();
        setTheme(mUsingDarkText ? R.style.RecentsTheme_Wallpaper_Light
                : R.style.RecentsTheme_Wallpaper);

        // Set the Recents layout
        setContentView(R.layout.recents);
        takeKeyEvents(true);
        mRecentsView = findViewById(R.id.recents_view);
        mScrimViews = new SystemBarScrimViews(this);
        getWindow().getAttributes().privateFlags |=
                WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
        if (Recents.getConfiguration().isLowRamDevice) {
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        }

        mLastConfig = new Configuration(Utilities.getAppConfiguration(this));

        // Set the window background
        mRecentsView.updateBackgroundScrim(getWindow(), isInMultiWindowMode());

        // Create the home intent runnable
        mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
        mHomeIntent.addCategory(Intent.CATEGORY_HOME);
        mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);

        // Register the broadcast receiver to handle messages when the screen is turned off
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_USER_SWITCHED);
        registerReceiver(mSystemBroadcastReceiver, filter);

        getWindow().addPrivateFlags(LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION);
    }

 我们知道加载的layout :setContentView(R.layout.recents);

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Recents View -->
    <com.android.systemui.recents.views.RecentsView
        android:id="@+id/recents_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </com.android.systemui.recents.views.RecentsView>

    <!-- Incompatible task overlay -->
    <ViewStub android:id="@+id/incompatible_app_overlay_stub"
        android:inflatedId="@+id/incompatible_app_overlay"
        android:layout="@layout/recents_incompatible_app_overlay"
        android:layout_width="match_parent"
        android:layout_height="128dp"
        android:layout_gravity="center_horizontal|top" />

    <!-- Nav Bar Scrim View -->
    <ImageView
        android:id="@+id/nav_bar_scrim"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal|bottom"
        android:scaleType="fitXY"
        android:src="@drawable/recents_lower_gradient" />
</FrameLayout>

 

 protected void onStart() {
        super.onStart();
	  

        // Reload the stack view whenever we are made visible again
        reloadStackView();

        // Notify that recents is now visible
        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
        MetricsLogger.visible(this, MetricsEvent.OVERVIEW_ACTIVITY);

        // Getting system scrim colors ignoring wallpaper visibility since it should never be grey.
        ColorExtractor.GradientColors systemColors = mColorExtractor.getColors(
                ColorExtractor.TYPE_DARK, WallpaperManager.FLAG_SYSTEM, true);
        // We don't want to interpolate colors because we're defining the initial state.
        // Gradient should be set/ready when you open "Recents".
        mRecentsView.setScrimColors(systemColors, false);

        // Notify of the next draw
        mRecentsView.getViewTreeObserver().addOnPreDrawListener(mRecentsDrawnEventListener);

        // If Recents was restarted, then it should complete the enter animation with partially
        // reset launch state with dock, app and home set to false
        Object isRelaunching = getLastNonConfigurationInstance();
        if (isRelaunching != null && isRelaunching instanceof Boolean && (boolean) isRelaunching) {
            RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
            launchState.launchedViaDockGesture = false;
            launchState.launchedFromApp = false;
            launchState.launchedFromHome = false;
            onEnterAnimationComplete();
        }
        mRecentsStartRequested = false;

	
    }
 private void reloadStackView() {
        // 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 = new RecentsTaskLoadPlan(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);
        }

        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
        loadOpts.runningTaskId = launchState.launchedToTaskId;
        loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
        loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
        loader.loadTasks(loadPlan, loadOpts);
        TaskStack stack = loadPlan.getTaskStack();
	
        mRecentsView.onReload(stack, mIsVisible);

        // Update the nav bar scrim, but defer the animation until the enter-window event
        boolean animateNavBarScrim = !launchState.launchedViaDockGesture;
        mScrimViews.updateNavBarScrim(animateNavBarScrim, stack.getTaskCount() > 0, null);

        // If this is a new instance relaunched by AM, without going through the normal mechanisms,
        // then we have to manually trigger the enter animation state
        boolean wasLaunchedByAm = !launchState.launchedFromHome &&
                !launchState.launchedFromApp;
        if (wasLaunchedByAm) {
            EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
        }

        // Keep track of whether we launched from the nav bar button or via alt-tab
        if (launchState.launchedWithAltTab) {
            MetricsLogger.count(this, "overview_trigger_alttab", 1);
        } else {
            MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
        }

        // Keep track of whether we launched from an app or from home
        if (launchState.launchedFromApp) {
            Task launchTarget = stack.getLaunchTarget();
            int launchTaskIndexInStack = launchTarget != null
                    ? stack.indexOfTask(launchTarget)
                    : 0;
            MetricsLogger.count(this, "overview_source_app", 1);
            // If from an app, track the stack index of the app in the stack (for affiliated tasks)
            MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
        } else {
            MetricsLogger.count(this, "overview_source_home", 1);
        }

        // Keep track of the total stack task count
        int taskCount = mRecentsView.getStack().getTaskCount();
        MetricsLogger.histogram(this, "overview_task_count", taskCount);

        // After we have resumed, set the visible state until the next onStop() call
        mIsVisible = true;
   		
    }

 1.加载资源,我们知道 loader.preloadTasks这个函数是加载资源的。

  2.mRecentsView.onReload(stack, mIsVisible);

  public void onReload(TaskStack stack, boolean isResumingFromVisible) {
     
        final RecentsConfiguration config = Recents.getConfiguration();
        final RecentsActivityLaunchState launchState = config.getLaunchState();
        final boolean isTaskStackEmpty = stack.getTaskCount() == 0;

        if (mTaskStackView == null) {
            isResumingFromVisible = false;
            mTaskStackView = new TaskStackView(getContext());
            mTaskStackView.setSystemInsets(mSystemInsets);
            addView(mTaskStackView);//把TaskStackView添加到RecentView结构树中
        }

        // Reset the state
        mAwaitingFirstLayout = !isResumingFromVisible;

        // Update the stack
        mTaskStackView.onReload(isResumingFromVisible);
        updateStack(stack, true /* setStackViewTasks */);
        updateBusyness();

        if (isResumingFromVisible) {
            // If we are already visible, then restore the background scrim
            animateBackgroundScrim(getOpaqueScrimAlpha(), DEFAULT_UPDATE_SCRIM_DURATION);
        } else {
            // If we are already occluded by the app, then set the final background scrim alpha now.
            // Otherwise, defer until the enter animation completes to animate the scrim alpha with
            // the tasks for the home animation.
            if (launchState.launchedViaDockGesture || launchState.launchedFromApp
                    || isTaskStackEmpty) {
                mBackgroundScrim.setAlpha((int) (getOpaqueScrimAlpha() * 255));
            } else {
                mBackgroundScrim.setAlpha(0);
            }
            mMultiWindowBackgroundScrim.setAlpha(mBackgroundScrim.getAlpha());
        }
	
    }

 上述比较关键的代码是把  addView(mTaskStackView);//把TaskStackView添加到RecentView结构树中

 void onReload(boolean isResumingFromVisible) {
    
        if (!isResumingFromVisible) {
            // Reset the focused task
            resetFocusedTask(getFocusedTask());
        }

        // Reset the state of each of the task views
        List<TaskView> taskViews = new ArrayList<>();
 Log.i("Systemui-xiao","TaskStackView   onReload getTaskViews size:"+(getTaskViews()==null?getTaskViews():getTaskViews().size())+",mViewPool.getViews size:"+(mViewPool.getViews()==null?mViewPool.getViews():mViewPool.getViews().size()));		
        taskViews.addAll(getTaskViews());
        taskViews.addAll(mViewPool.getViews());
        for (int i = taskViews.size() - 1; i >= 0; i--) {
            taskViews.get(i).onReload(isResumingFromVisible);
        }

        // Reset the stack state
        readSystemFlags();
        mTaskViewsClipDirty = true;
        mUIDozeTrigger.stopDozing();
        if (!isResumingFromVisible) {
            mStackScroller.reset();
            mStableLayoutAlgorithm.reset();
            mLayoutAlgorithm.reset();
            mLastScrollPPercent = -1;
        }

        // Since we always animate to the same place in (the initial state), always reset the stack
        // to the initial state when resuming
        mStackReloaded = true;
        mFinishedLayoutAfterStackReload = false;
        mLaunchNextAfterFirstMeasure = false;
        mInitialState = INITIAL_STATE_UPDATE_ALL;
        requestLayout();
	 	
    }

 数据加载完,父布局TaskStackView也添加完了,子布局TaskView(近期任务)什么时候才会加载呢?

2.TaskStackView .onMeasure

 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
        mInMeasureLayout = true;
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
	 Log.i("Systemui-xiao","TaskStackView   onMeasure");	

        // Update the stable stack bounds, but only update the current stack bounds if the stable
        // bounds have changed.  This is because we may get spurious measures while dragging where
        // our current stack bounds reflect the target drop region.
        mLayoutAlgorithm.getTaskStackBounds(mDisplayRect, new Rect(0, 0, width, height),
                mLayoutAlgorithm.mSystemInsets.top, mLayoutAlgorithm.mSystemInsets.left,
                mLayoutAlgorithm.mSystemInsets.right, mTmpRect);
        if (!mTmpRect.equals(mStableStackBounds)) {
            mStableStackBounds.set(mTmpRect);
            mStackBounds.set(mTmpRect);
            mStableWindowRect.set(0, 0, width, height);
            mWindowRect.set(0, 0, width, height);
        }

        // Compute the rects in the stack algorithm
        mStableLayoutAlgorithm.initialize(mDisplayRect, mStableWindowRect, mStableStackBounds);
        mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds);
        updateLayoutAlgorithm(false /* boundScroll */);

        // If this is the first layout, then scroll to the front of the stack, then update the
        // TaskViews with the stack so that we can lay them out
        boolean resetToInitialState = (width != mLastWidth || height != mLastHeight)
                && mResetToInitialStateWhenResized;
        if (!mFinishedLayoutAfterStackReload || mInitialState != INITIAL_STATE_UPDATE_NONE
                || resetToInitialState) {
            if (mInitialState != INITIAL_STATE_UPDATE_LAYOUT_ONLY || resetToInitialState) {
                updateToInitialState();//最终会执行onStackScrollChanged
                mResetToInitialStateWhenResized = false;
            }
            if (mFinishedLayoutAfterStackReload) {
                mInitialState = INITIAL_STATE_UPDATE_NONE;
            }
        }
        // If we got the launch-next event before the first layout pass, then re-send it after the
        // initial state has been updated
        if (mLaunchNextAfterFirstMeasure) {
            mLaunchNextAfterFirstMeasure = false;
            EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
        }

        // Rebind all the views, including the ignore ones
     
        bindVisibleTaskViews(mStackScroller.getStackScroll(), false /* ignoreTaskOverrides */);
		

        // Measure each of the TaskViews
        mTmpTaskViews.clear();
        mTmpTaskViews.addAll(getTaskViews());
        mTmpTaskViews.addAll(mViewPool.getViews());
        int taskViewCount = mTmpTaskViews.size();
        for (int i = 0; i < taskViewCount; i++) {
            measureTaskView(mTmpTaskViews.get(i));
        }
        if (mTaskViewFocusFrame != null) {
            mTaskViewFocusFrame.measure();
        }

        setMeasuredDimension(width, height);
        mLastWidth = width;
        mLastHeight = height;
        mInMeasureLayout = false;
    }
 void bindVisibleTaskViews(float targetStackScroll) { 	
        bindVisibleTaskViews(targetStackScroll, false /* ignoreTaskOverrides */);
    }
 void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) {
        // Get all the task transforms
       
        ArrayList<Task> tasks = mStack.getTasks();
        int[] visibleTaskRange = computeVisibleTaskTransforms(mCurrentTaskTransforms, tasks,
                mStackScroller.getStackScroll(), targetStackScroll, mIgnoreTasks,
                ignoreTaskOverrides);

        // Return all the invisible children to the pool
        mTmpTaskViewMap.clear();
        List<TaskView> taskViews = getTaskViews();
        int lastFocusedTaskIndex = -1;
        int taskViewCount = taskViews.size();
	
        for (int i = taskViewCount - 1; i >= 0; i--) {
            TaskView tv = taskViews.get(i);
            Task task = tv.getTask();

            // Skip ignored tasks
            if (mIgnoreTasks.contains(task.key)) {
                continue;
            }

            // It is possible for the set of lingering TaskViews to differ from the stack if the
            // stack was updated before the relayout.  If the task view is no longer in the stack,
            // then just return it back to the view pool.
            int taskIndex = mStack.indexOfTask(task);
            TaskViewTransform transform = null;
            if (taskIndex != -1) {
                transform = mCurrentTaskTransforms.get(taskIndex);
            }

            if (transform != null && transform.visible) {
                mTmpTaskViewMap.put(task.key, tv);
            } else {
                if (mTouchExplorationEnabled && Utilities.isDescendentAccessibilityFocused(tv)) {
                    lastFocusedTaskIndex = taskIndex;
                    resetFocusedTask(task);
                }
                mViewPool.returnViewToPool(tv);
            }
        }

        // Pick up all the newly visible children
        for (int i = tasks.size() - 1; i >= 0; i--) {
            Task task = tasks.get(i);
            TaskViewTransform transform = mCurrentTaskTransforms.get(i);

            // Skip ignored tasks
            if (mIgnoreTasks.contains(task.key)) {
                continue;
            }

            // Skip the invisible stack tasks
            if (!transform.visible) {
                continue;
            }

            TaskView tv = mTmpTaskViewMap.get(task.key);
				
            if (tv == null) {
			
                tv = mViewPool.pickUpViewFromPool(task, task);//创建taskview
                if (transform.rect.top <= mLayoutAlgorithm.mStackRect.top) {
                    updateTaskViewToTransform(tv, mLayoutAlgorithm.getBackOfStackTransform(),
                            AnimationProps.IMMEDIATE);
                } else {
                    updateTaskViewToTransform(tv, mLayoutAlgorithm.getFrontOfStackTransform(),
                            AnimationProps.IMMEDIATE);
                }
            } else {
                // Reattach it in the right z order
                final int taskIndex = mStack.indexOfTask(task);
                final int insertIndex = findTaskViewInsertIndex(task, taskIndex);
                if (insertIndex != getTaskViews().indexOf(tv)){
                    detachViewFromParent(tv);
                    attachViewToParent(tv, insertIndex, tv.getLayoutParams());
				 Log.i("Systemui-xiao","TaskStackView  bindVisibleTaskViews updateTaskViewsList"); 
                    updateTaskViewsList();
                }
            }
        }

        updatePrefetchingTask(tasks, visibleTaskRange[0], visibleTaskRange[1]);

        // Update the focus if the previous focused task was returned to the view pool
        if (lastFocusedTaskIndex != -1) {
            int newFocusedTaskIndex = (lastFocusedTaskIndex < visibleTaskRange[1])
                    ? visibleTaskRange[1]
                    : visibleTaskRange[0];
            setFocusedTask(newFocusedTaskIndex, false /* scrollToTask */,
                    true /* requestViewFocus */);
            TaskView focusedTaskView = getChildViewForTask(mFocusedTask);
            if (focusedTaskView != null) {
                focusedTaskView.requestAccessibilityFocus();
            }
        }
	 	
    }

 

 V pickUpViewFromPool(T preferredData, T prepareData) {
        V v = null;
        boolean isNewView = false;
        if (mPool.isEmpty()) {
            v = mViewCreator.createView(mContext);
            isNewView = true;
        } else {
            // Try and find a preferred view
            Iterator<V> iter = mPool.iterator();
            while (iter.hasNext()) {
                V vpv = iter.next();
                if (mViewCreator.hasPreferredData(vpv, preferredData)) {
                    v = vpv;
                    iter.remove();
                    break;
                }
            }
            // Otherwise, just grab the first view
            if (v == null) {
                v = mPool.pop();
            }
        }
        mViewCreator.onPickUpViewFromPool(v, prepareData, isNewView);
        return v;
    }

 

 public TaskView createView(Context context) {
     	
        if (Recents.getConfiguration().isGridEnabled) {
            return (GridTaskView) mInflater.inflate(R.layout.recents_grid_task_view, this, false);
        } else {
            return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false);
        }
    }

 此次近期任务界面被创建成功。那我们看看recents_task_view

<com.android.systemui.recents.views.TaskView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:focusable="true">
    <com.android.systemui.recents.views.TaskViewThumbnail
        android:id="@+id/task_view_thumbnail"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <include layout="@layout/recents_task_view_header" />

    <!-- TODO: Move this into a view stub -->
    <include layout="@layout/recents_task_view_lock_to_app"/>

    <!-- The incompatible app toast -->
    <include layout="@layout/recents_task_view_incompatible_app_toast"/>
</com.android.systemui.recents.views.TaskView>

 TaskViewThumbnail这个是显示近期任务图片的View。

recents_task_view_header是图片上面部分也就是头部。

大家再结合我开头给的图片来看就能比较直观形象。

 mRecentsView.getViewTreeObserver().addOnPreDrawListener(mRecentsDrawnEventListener);

private final OnPreDrawListener mRecentsDrawnEventListener =
            new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
            
                    mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
                    EventBus.getDefault().post(new RecentsDrawnEvent());
                    if (LatencyTracker.isEnabled(getApplicationContext())) {
                        DejankUtils.postAfterTraversal(() -> LatencyTracker.getInstance(
                                getApplicationContext()).onActionEnd(
                                LatencyTracker.ACTION_TOGGLE_RECENTS));
                    }
                    DejankUtils.postAfterTraversal(() -> {
                        Recents.getTaskLoader().startLoader(RecentsActivity.this);
                        Recents.getTaskLoader().getHighResThumbnailLoader().setVisible(true);
                    });
                    return true;
                }
            };

此处近期任务列表算讲解完成,有点粗糙,只讲了界面的加载流程,至于细节数据加载和显示都未涉及到,到时候有时间再详细讲解补充

 

 

  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值