本文流程基于Android 9.0
PhoneWindowManager
处理点击事件的方法是interceptKeyBeforeDispatching()
。相应recents键的逻辑如下:
点击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;
}
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) {}
}
}
// 接口其他一些方法的实现
};
可以看出,调用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);
}
}
}
}
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回收掉。
参考:Android输入事件从读取到分发五:事件分发前的拦截过程