本文流程基于Android 9.0
preloadRecentApps()流程介绍
1. PhoneWindowManager的事件分发
PhoneWindowManager
处理点击事件的方法是interceptKeyBeforeDispatching()
。相应处理逻辑如下: 点击switch
键
} else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) { //不处于锁屏界面 if (!keyguardOn) { //ACTION_DOWN并且只点了一次 if (down && repeatCount == 0) { //预加载recent preloadRecentApps(); } else if (!down) { //当不处于ACTION_DOWN时,可能是up或者cancel toggleRecentApps(); } } return -1;}复制代码
2.调用StatusBaraManbagerService的preloadRecentApps
private void preloadRecentApps() { mPreloadedRecentApps = true; StatusBarManagerInternal statusbar = getStatusBarManagerInternal(); if (statusbar != null) { //调用statusbar的preloadRecentApps() statusbar.preloadRecentApps(); }}复制代码
这里的是statusbar
是通过getStatusBarManagerInternal()
获取的,getStatusBarManagerInternal()
实现如下:
StatusBarManagerInternal getStatusBarManagerInternal() { synchronized (mServiceAquireLock) { if (mStatusBarManagerInternal == null) { mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class); } return mStatusBarManagerInternal; }}复制代码
那么通过LocalServices.getService()
是怎么得到的呢?其实在StatusBarManagerService
初始化的时候,就将StatusBarManagerInernal
的实现加进了LocalService
中。
/** * Construct the service, add the status bar view to the window manager */public StatusBarManagerService(Context context, WindowManagerService windowManager) { mContext = context; mWindowManager = windowManager; LocalServices.addService(StatusBarManagerInternal.class, mInternalService); LocalServices.addService(GlobalActionsProvider.class, mGlobalActionsProvider);}复制代码
mInternalService
的实现如下:
/** * Private API used by NotificationManagerService. */private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() { private boolean mNotificationLightOn; // 接口其他一些方法的实现 @Override public void toggleRecentApps() { if (mBar != null) { try { mBar.toggleRecentApps(); } catch (RemoteException ex) {} } } @Override public void setCurrentUser(int newUserId) { if (SPEW) Slog.d(TAG, "Setting current user to user " + newUserId); mCurrentUserId = newUserId; } @Override public void preloadRecentApps() { if (mBar != null) { try { mBar.preloadRecentApps(); } catch (RemoteException ex) {} } } @Override public void cancelPreloadRecentApps() { if (mBar != null) { try { mBar.cancelPreloadRecentApps(); } catch (RemoteException ex) {} } } @Override public void showRecentApps(boolean triggeredFromAltTab) { if (mBar != null) { try { mBar.showRecentApps(triggeredFromAltTab); } catch (RemoteException ex) {} } } @Override public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { if (mBar != null) { try { mBar.hideRecentApps(triggeredFromAltTab, triggeredFromHomeKey); } catch (RemoteException ex) {} } } // 接口其他一些方法的实现};复制代码
3.跨进程调用preloadRecentApps的实际实现
可以看出,调用mInternalService
实际上是调用mBar:IStatusBar
的方法。
mBar
对应的对象时什么呢?
// ================================================================================// Callbacks from the status bar service.// ================================================================================@Overridepublic void registerStatusBar(IStatusBar bar, ListiconSlots, List iconList, int switches[], List binders, Rect fullscreenStackBounds, Rect dockedStackBounds) { enforceStatusBarService(); Slog.i(TAG, "registerStatusBar bar=" + bar); mBar = bar; // code...}复制代码
registerStatusBar
在哪里调用呢?在StatusBar
的start()
方法中调用到:
@Overridepublic void start() { //code... try { mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders, fullscreenStackBounds, dockedStackBounds); } catch (RemoteException ex) { // If the system process isn't there we're doomed anyway. } //code...}复制代码
CommandQueue
其实是IStatusBar
的一个代理类,调用mCommandQueue
的preloadRecentApps()
方法其实是调用CommandQueue
内部接口Callbacks
的preloadRecentApps()
,而实现CommandQueue.Callbacks
是在Recents
类中实现的,对应的preloadRecentApps()
的实际实现也是在Recents
中,具体实现如下:
/** * Preloads info for the Recents activity. */@Overridepublic 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为RecentsImpl,调用的实际上是RecentsImpl的preloadRecents() 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); } } }}复制代码
4.从AMS中获取任务队列,加入到任务栈TaskStack中
RecentsImpl
的preloadRecents
方法:
public void preloadRecents() { //如果屏幕固定,直接返回,屏幕固定功能是在Android5.1上增加的,当打开此功能时,不允许切换到其他界面 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)) { ActivityManager.RunningTaskInfo runningTask = ActivityManagerWrapper.getInstance().getRunningTask(); //如果获取到的runningTask(运行着的任务)为null时,则直接返回 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
的preloadTasks()
方法:
/** Preloads recents tasks using the specified plan to store the output. */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(); }}复制代码
RecentsTaskLoadPlan
的preloadPlan()
方法,将从AMS中获取的任务队列加入到任务栈TaskStack
中:
/** * 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. * * The tasks will be ordered by: * - least-recent to most-recent stack tasks * * Note: Do not lock, since this can be calling back to the loader, which separately also drives * this call (callers should synchronize on the loader before making this call). */public void preloadPlan(PreloadOptions opts, RecentsTaskLoader loader, int runningTaskId, int currentUserId) { Resources res = mContext.getResources(); ArrayListallTasks = new ArrayList<>(); if (mRawTasks == null) { //从ActivityManagerService中获取原始的任务列表 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(); //通过遍历,将RawTask变为最近任务中可以用于加载显式的Task类型,最终加入到最近任务栈中mStack 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 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(); //这里是预先将任务队列加载显式到TaskStackView上 mStack.setTasks(allTasks, false /* notifyStackChanges */);}复制代码
注意:保存任务队列的都是一些临时变量,会伴随着gc回收掉。
参考: