引言
QuickStep是Android P中一大新特性质礼,在Android P中 Google將SystemUI中Recents在Launcher又實現(xiàn)了一遍服鹅,并且取名為QuickStep逻卖,其具有新的界面(如下圖)以及交互方式粘捎。
QuickStep有兩種啟動方式形纺,一種為常規(guī)啟動方式——點(diǎn)擊recents鍵丘侠,一種則是通過手勢啟動(如下圖)。
那么QuickStep是如何啟動的呢逐样?是如何加載數(shù)據(jù)的呢蜗字?今天就先來講講常規(guī)啟動的流程。
常規(guī)啟動
用戶通過導(dǎo)航欄Recents Button啟動脂新,首先來看下Recents Button的onClick事件如何實現(xiàn):
private void onRecentsClick(View v) {
if (LatencyTracker.isEnabled(getContext())) {
LatencyTracker.getInstance(getContext()).onActionStart(
LatencyTracker.ACTION_TOGGLE_RECENTS);
}
mStatusBar.awakenDreams();
mCommandQueue.toggleRecentApps();
}
這里通過CommandQueue發(fā)出啟動Recents的消息挪捕。而Recents.java使用了CommandQueue的callback接口:
/**
* Toggles the Recents activity.
*/
@Override
public void toggleRecentApps() {
// Ensure the device has been provisioned before allowing the user to interact with
// recents
if (!isUserSetup()) {
return;
}
// 啟動Launcher QuickStep 9.0新特性
// 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;
}
// SystemUI 默認(rèn)啟動Recents
int growTarget = getComponent(Divider.class).getView().growsRecents();
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
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);
}
}
}
}
可以看出在9.0中,SystemUI啟動Recents的地方會分成兩部分争便,一個是通過AIDL跨進(jìn)程啟動QuickStep级零,另外一種則是啟動SystemUI內(nèi)部默認(rèn)的Recents。
OverviewProxyService
OverviewProxyService就是實現(xiàn)與含有QuickStep功能的Launcher進(jìn)行數(shù)據(jù)傳輸?shù)墓δ茴愔鸵遥饕獙崿F(xiàn)了與Service(Launcher)端的綁定奏纪,并將SystemUIProxy傳給Launcher鉴嗤,供Launcher內(nèi)部調(diào)用
SystemUI接口。
OverviewProxyService開機(jī)時嘗試bindService:
private void internalConnectToCurrentUser() {
disconnectFromLauncherService();
// If user has not setup yet or already connected, do not try to connect
if (!mDeviceProvisionedController.isCurrentUserSetup() || !isEnabled()) {
Log.v(TAG_OPS, "Cannot attempt connection, is setup "
+ mDeviceProvisionedController.isCurrentUserSetup() + ", is enabled "
+ isEnabled());
return;
}
mHandler.removeCallbacks(mConnectionRunnable);
Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
.setPackage(mRecentsComponentName.getPackageName());
boolean bound = false;
try {
bound = mContext.bindServiceAsUser(launcherServiceIntent,
mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
} catch (SecurityException e) {
Log.e(TAG_OPS, "Unable to bind because of security error", e);
}
if (bound) {
// Ensure that connection has been established even if it thinks it is bound
mHandler.postDelayed(mDeferredConnectionCallback, DEFERRED_CALLBACK_MILLIS);
} else {
// Retry after exponential backoff timeout
final long timeoutMs = (long) Math.scalb(BACKOFF_MILLIS, mConnectionBackoffAttempts);
mHandler.postDelayed(mConnectionRunnable, timeoutMs);
mConnectionBackoffAttempts++;
Log.w(TAG_OPS, "Failed to connect on attempt " + mConnectionBackoffAttempts
+ " will try again in " + timeoutMs + "ms");
}
}
并在ServiceConnect之后將SystemUIProxy傳給Launcher端序调,SystemUIProxy主要實現(xiàn)功能接口如下:
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
public GraphicBufferCompat screenshot(Rect sourceCrop, int width, int height, int minLayer,
int maxLayer, boolean useIdentityTransform, int rotation) {
long token = Binder.clearCallingIdentity();
try {
return new GraphicBufferCompat(SurfaceControl.screenshotToBuffer(sourceCrop, width,
height, minLayer, maxLayer, useIdentityTransform, rotation));
} finally {
Binder.restoreCallingIdentity(token);
}
}
public void startScreenPinning(int taskId) {
long token = Binder.clearCallingIdentity();
try {
mHandler.post(() -> {
StatusBar statusBar = ((SystemUIApplication) mContext).getComponent(
StatusBar.class);
if (statusBar != null) {
statusBar.showScreenPinningRequest(taskId, false /* allowCancel */);
}
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
public void onSplitScreenInvoked() {
long token = Binder.clearCallingIdentity();
try {
EventBus.getDefault().post(new DockedFirstAnimationFrameEvent());
} finally {
Binder.restoreCallingIdentity(token);
}
}
public void onOverviewShown(boolean fromHome) {
long token = Binder.clearCallingIdentity();
try {
mHandler.post(() -> {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onOverviewShown(fromHome);
}
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
public void setInteractionState(@InteractionType int flags) {
long token = Binder.clearCallingIdentity();
try {
if (mInteractionFlags != flags) {
mInteractionFlags = flags;
mHandler.post(() -> {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onInteractionFlagsChanged(flags);
}
});
}
} finally {
Prefs.putInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS, mInteractionFlags);
Binder.restoreCallingIdentity(token);
}
}
public Rect getNonMinimizedSplitScreenSecondaryBounds() {
long token = Binder.clearCallingIdentity();
try {
Divider divider = ((SystemUIApplication) mContext).getComponent(Divider.class);
if (divider != null) {
return divider.getView().getNonMinimizedSplitScreenSecondaryBounds();
}
return null;
} finally {
Binder.restoreCallingIdentity(token);
}
}
public void setBackButtonAlpha(float alpha, boolean animate) {
long token = Binder.clearCallingIdentity();
try {
mHandler.post(() -> {
notifyBackButtonAlphaChanged(alpha, animate);
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
};
這里不過多解讀跨進(jìn)程的具體功能醉锅,后續(xù)對QuickStep單獨(dú)功介紹時進(jìn)行一一介紹。
TouchInteractionService
TouchInteractionService就是Launcher端的Service发绢,代碼路徑是:packages/apps/Launcher3/quickstep/src/com/android/quickstep/TouchInteractionService.java硬耍,通過SystemUI中bind啟動。前面SystemUI在Recents中通過AIDL調(diào)用Launcher端onOverviewToggle:
@Override
public void onOverviewToggle() {
mOverviewCommandHelper.onOverviewToggle();
}
這里的mOverviewCommandHelper對應(yīng)的就是OverviewCommandHelper边酒,其代碼路徑是:
packages/apps/Launcher3/quickstep/src/com/android/quickstep/OverviewCommandHelper.java默垄,其作用主要是啟動Launcher中的QuickStep界面。代碼邏輯詳情如下:
public void onOverviewToggle() {
// If currently screen pinning, do not enter overview
if (mAM.isScreenPinningActive()) {
return;
}
mAM.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
mMainThreadExecutor.execute(new RecentsActivityCommand<>());
}
這里最關(guān)鍵的邏輯就是mMainThreadExecutor.execute(new RecentsActivityCommand<>());這里RecentsActivityCommand是OverviewCommandHelper中的一個內(nèi)部類甚纲,因為implement了Runnable口锭,因此mMainThreadExecutor主要執(zhí)行的就是該類的run方法,其代碼邏輯如下:
@Override
public void run() {
long elapsedTime = mCreateTime - mLastToggleTime;
mLastToggleTime = mCreateTime;
if (!handleCommand(elapsedTime)) {
// Start overview
if (!mHelper.switchToRecentsIfVisible(true)) {
mListener = mHelper.createActivityInitListener(this::onActivityReady);
mListener.registerAndStartActivity(overviewIntent, this::createWindowAnimation,
Context, mMainThreadExecutor.getHandler(), RECENTS_LAUNCH_DURATION);
}
}
}
這里邏輯分成兩塊介杆,一是連續(xù)點(diǎn)擊判斷和實現(xiàn)雙擊切換應(yīng)用鹃操,這塊邏輯在handleCommand(elapsedTime),二是啟動QuickStep對應(yīng)的Activity界面春哨,并執(zhí)行對應(yīng)的窗口動畫荆隘,這里的overviewIntent是在OverviewCommandHelper初始化的時候進(jìn)行初始化的,其關(guān)鍵代碼邏輯如下:
private void initOverviewTargets() {
ComponentName defaultHome = PackageManagerWrapper.getInstance()
.getHomeActivities(new ArrayList<>());
final String overviewIntentCategory;
//判斷默認(rèn)Launcher是否為自身
if (defaultHome == null || mMyHomeComponent.equals(defaultHome)) {
// User default home is same as out home app. Use Overview integrated in Launcher.
overviewComponent = mMyHomeComponent;
mActivityControlHelper = new LauncherActivityControllerHelper();
overviewIntentCategory = Intent.CATEGORY_HOME;
if (mUpdateRegisteredPackage != null) {
// Remove any update listener as we don't care about other packages.
mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
mUpdateRegisteredPackage = null;
}
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
overviewComponent = new ComponentName(mContext, RecentsActivity.class);
mActivityControlHelper = new FallbackActivityControllerHelper(defaultHome);
overviewIntentCategory = Intent.CATEGORY_DEFAULT;
// User's default home app can change as a result of package updates of this app (such
// as uninstalling the app or removing the "Launcher" feature in an update).
// Listen for package updates of this app (and remove any previously attached
// package listener).
if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) {
if (mUpdateRegisteredPackage != null) {
mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
}
mUpdateRegisteredPackage = defaultHome.getPackageName();
IntentFilter updateReceiver = new IntentFilter(ACTION_PACKAGE_ADDED);
updateReceiver.addAction(ACTION_PACKAGE_CHANGED);
updateReceiver.addAction(ACTION_PACKAGE_REMOVED);
updateReceiver.addDataScheme("package");
updateReceiver.addDataSchemeSpecificPart(mUpdateRegisteredPackage,
PatternMatcher.PATTERN_LITERAL);
mContext.registerReceiver(mOtherHomeAppUpdateReceiver, updateReceiver);
}
}
overviewIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(overviewIntentCategory)
.setComponent(overviewComponent)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
這里的邏輯主要為判斷當(dāng)前系統(tǒng)默認(rèn)Launcher是不是Launcher3自身赴背,如果是將自身component賦給
overviewComponent椰拒,并反注冊Launcher切換廣播接受者;如果不是凰荚,則將RecentsActivity的component賦給overviewComponent燃观,兩者的區(qū)別就是啟動的Recents界面一個帶有hotseat(Launcher3獨(dú)有),一個則是普通的Recents界面便瑟。
總結(jié)
到這里QuickStep的常規(guī)按鍵啟動流程介紹結(jié)束了缆毁,下面附上一張對應(yīng)的時序圖工大家參考:
下一篇,將給大家?guī)硎謩輪臃治觥?/p>
本篇文章已獨(dú)家授權(quán)公眾號ApeClub使用到涂,轉(zhuǎn)載請注明出處脊框!更多好文章請關(guān)注公眾號ApeClub。