本文流程基於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.
// ================================================================================
@Override
public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
Rect fullscreenStackBounds, Rect dockedStackBounds) {
enforceStatusBarService();
Slog.i(TAG, "registerStatusBar bar=" + bar);
mBar = bar;
// code...
}
複製程式碼
registerStatusBar
在哪裡呼叫呢?在StatusBar
的start()
方法中呼叫到:
@Override
public 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.
*/
@Override
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為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();
ArrayList<Task> allTasks = 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回收掉。