developer.android.com/develop/ui/…
startingWindow移除的堆栈
05-27 14:32:25.969 724 755 D WindowManager: java.lang.Exception
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.SurfaceAnimator.createAnimationLeash(SurfaceAnimator.java:464)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.SurfaceAnimator.startAnimation(SurfaceAnimator.java:187)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowContainer.startAnimation(WindowContainer.java:2961)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowContainer.startAnimation(WindowContainer.java:2968)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowContainer.startAnimation(WindowContainer.java:2974)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.TaskOrganizerController.applyStartingWindowAnimation(TaskOrganizerController.java:616)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.TaskOrganizerController.removeStartingWindow(TaskOrganizerController.java:696)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.StartingSurfaceController$StartingSurface.remove(StartingSurfaceController.java:271)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.ActivityRecord.removeStartingWindowAnimation(ActivityRecord.java:3038)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.ActivityRecord.removeStartingWindow(ActivityRecord.java:2955)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.ActivityRecord.onFirstWindowDrawn(ActivityRecord.java:6969)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowState.performShowLocked(WindowState.java:4418)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowStateAnimator.commitFinishDrawingLocked(WindowStateAnimator.java:259)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.DisplayContent.lambda$new$8(DisplayContent.java:1020)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.DisplayContent.$r8$lambda$-dBz3LtsWovWVT0SE5m__EwNfT4(DisplayContent.java:0)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.DisplayContent$$ExternalSyntheticLambda32.accept(R8$$SyntheticClass:0)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowContainer$ForAllWindowsConsumerWrapper.apply(WindowContainer.java:2834)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowContainer$ForAllWindowsConsumerWrapper.apply(WindowContainer.java:2824)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowState.applyInOrderWithImeWindows(WindowState.java:4652)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowState.forAllWindows(WindowState.java:4517)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1800)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1800)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1800)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1800)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1800)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1800)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1800)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1800)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1817)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.DisplayContent.applySurfaceChangesTransaction(DisplayContent.java:5196)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.RootWindowContainer.applySurfaceChangesTransaction(RootWindowContainer.java:985)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.RootWindowContainer.performSurfacePlacementNoTrace(RootWindowContainer.java:785)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.RootWindowContainer.performSurfacePlacement(RootWindowContainer.java:751)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop(WindowSurfacePlacer.java:177)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:126)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:115)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.wm.WindowSurfacePlacer$Traverser.run(WindowSurfacePlacer.java:57)
05-27 14:32:25.969 724 755 D WindowManager: at android.os.Handler.handleCallback(Handler.java:991)
05-27 14:32:25.969 724 755 D WindowManager: at android.os.Handler.dispatchMessage(Handler.java:102)
05-27 14:32:25.969 724 755 D WindowManager: at android.os.Looper.loopOnce(Looper.java:232)
05-27 14:32:25.969 724 755 D WindowManager: at android.os.Looper.loop(Looper.java:317)
05-27 14:32:25.969 724 755 D WindowManager: at android.os.HandlerThread.run(HandlerThread.java:85)
05-27 14:32:25.969 724 755 D WindowManager: at com.android.server.ServiceThread.run(ServiceThread.java:46)
普通的splashscreen和自定义动画的splashscreen的移除流程是一样的,只有在ActivityRecord.removeStartingWindow开始有区别
com/android/server/wm/ActivityRecord.java
void removeStartingWindow() {
boolean prevEligibleForLetterboxEducation = isEligibleForLetterboxEducation();
if (transferSplashScreenIfNeeded()) {
// 如果需要传递splashScreen到应用端,就返回了
return;
}
removeStartingWindowAnimation(true /* prepareAnimation */);
final Task task = getTask();
if (prevEligibleForLetterboxEducation != isEligibleForLetterboxEducation()
&& task != null) {
// Trigger TaskInfoChanged to update the letterbox education.
task.dispatchTaskInfoChangedIfNeeded(true /* force */);
}
}
private boolean transferSplashScreenIfNeeded() {
// 关键是mHandleExitSplashScreen是否为false,如果false,就直接返回了
if (finishing || !mHandleExitSplashScreen || mStartingSurface == null
|| mStartingWindow == null
|| mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH
// skip copy splash screen to client if it was resized
|| (mStartingData != null && mStartingData.mResizedFromTransfer)) {
return false;
}
if (isTransferringSplashScreen()) {
return true;
}
// Only do transfer after transaction has done when starting window exist.
if (mStartingData != null && mStartingData.mWaitForSyncTransactionCommit) {
mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_COPY_TO_CLIENT;
return true;
}
// 请求copy splashscreen
requestCopySplashScreen();
return isTransferringSplashScreen();
}
先假设mHandleExitSplashScreen是不为false,让流程走下去。回头来补相关的流程
private void requestCopySplashScreen() {
// 设置状态TRANSFER_SPLASH_SCREEN_COPYING
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_COPYING;
// mStartingSurface肯定不为空,调用copySplashScreenView
if (mStartingSurface == null || !mAtmService.mTaskOrganizerController.copySplashScreenView(
getTask(), mStartingSurface.mTaskOrganizer)) {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
removeStartingWindow();
}
scheduleTransferSplashScreenTimeout();
}
com/android/server/wm/TaskOrganizerController.java
boolean copySplashScreenView(Task task, ITaskOrganizer taskOrganizer) {
final Task rootTask = task.getRootTask();
if (rootTask == null) {
return false;
}
final ITaskOrganizer lastOrganizer = taskOrganizer != null ? taskOrganizer
: getTaskOrganizer();
if (lastOrganizer == null) {
return false;
}
try {
lastOrganizer.copySplashScreenView(task.mTaskId);
} catch (RemoteException e) {
Slog.e(TAG, "Exception sending copyStartingWindowView callback", e);
return false;
}
return true;
}
com/android/wm/shell/ShellTaskOrganizer.java
public void copySplashScreenView(int taskId) {
if (mStartingWindow != null) {
mStartingWindow.copySplashScreenView(taskId);
}
}
com/android/wm/shell/startingsurface/StartingWindowController.java
public void copySplashScreenView(int taskId) {
mSplashScreenExecutor.execute(() -> {
mStartingSurfaceDrawer.copySplashScreenView(taskId);
});
}
com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java
public void copySplashScreenView(int taskId) {
final StartingSurfaceDrawer.StartingWindowRecord record =
mStartingWindowRecordManager.getRecord(taskId);
final SplashWindowRecord preView = record instanceof SplashWindowRecord
? (SplashWindowRecord) record : null;
// 定义parcelable对象
SplashScreenView.SplashScreenViewParcelable parcelable;
SplashScreenView splashScreenView = preView != null ? preView.mSplashView : null;
if (splashScreenView != null && splashScreenView.isCopyable()) {
// 创建SplashScreenViewParcelable
parcelable = new SplashScreenView.SplashScreenViewParcelable(splashScreenView);
parcelable.setClientCallback(
new RemoteCallback((bundle) -> mSplashScreenExecutor.execute(
() -> onAppSplashScreenViewRemoved(taskId, false))));
splashScreenView.onCopied();
mAnimatedSplashScreenSurfaceHosts.append(taskId, splashScreenView.getSurfaceHost());
} else {
parcelable = null;
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
"Copying splash screen window view for task: %d with parcelable %b",
taskId, parcelable != null);
// 通知atms
ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable);
}
android/window/SplashScreenView.java
public SplashScreenViewParcelable(SplashScreenView view) {
final View iconView = view.getIconView();
mIconSize = iconView != null ? iconView.getWidth() : 0;
mBackgroundColor = view.getInitBackgroundColor();
mIconBackground = iconView != null ? copyDrawable(iconView.getBackground()) : null;
mSurfacePackage = view.mSurfacePackageCopy;
if (mSurfacePackage == null) {
// We only need to copy the drawable if we are not using a SurfaceView
mIconBitmap = iconView != null
? copyDrawable(((ImageView) view.getIconView()).getDrawable()) : null;
}
mBrandingBitmap = copyDrawable(view.getBrandingView().getBackground());
ViewGroup.LayoutParams params = view.getBrandingView().getLayoutParams();
mBrandingWidth = params.width;
mBrandingHeight = params.height;
if (view.getIconAnimationStart() != null) {
mIconAnimationStartMillis = view.getIconAnimationStart().toEpochMilli();
}
if (view.getIconAnimationDuration() != null) {
mIconAnimationDurationMillis = view.getIconAnimationDuration().toMillis();
}
}
就是给成员属性赋值而已,bitmap,size,color等
com/android/server/wm/ActivityTaskManagerService.java
public void onSplashScreenViewCopyFinished(int taskId,
@Nullable SplashScreenViewParcelable parcelable)
throws RemoteException {
mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_TASKS,
"copySplashScreenViewFinish()");
synchronized (mGlobalLock) {
final Task task = mRootWindowContainer.anyTaskForId(taskId,
MATCH_ATTACHED_TASK_ONLY);
if (task != null) {
final ActivityRecord r = task.getTopWaitSplashScreenActivity();
if (r != null) {
r.onCopySplashScreenFinish(parcelable);
}
}
}
}
com/android/server/wm/ActivityRecord.java
void onCopySplashScreenFinish(@Nullable SplashScreenViewParcelable parcelable) {
removeTransferSplashScreenTimeout();
final SurfaceControl windowAnimationLeash = (parcelable == null
|| mTransferringSplashScreenState != TRANSFER_SPLASH_SCREEN_COPYING
|| mStartingWindow == null || mStartingWindow.mRemoved
|| finishing) ? null
: TaskOrganizerController.applyStartingWindowAnimation(mStartingWindow);// 获取splashscreen对象的windowState的surfaceControl
if (windowAnimationLeash == null) {
// Unable to copy from shell, maybe it's not a splash screen, or something went wrong.
// Either way, abort and reset the sequence.
if (parcelable != null) {
parcelable.clearIfNeeded();
}
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
removeStartingWindow();
return;
}
try {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT;
//将token,parcel,splashscreen的leash传给app
mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
TransferSplashScreenViewStateItem.obtain(token, parcelable,
windowAnimationLeash));
scheduleTransferSplashScreenTimeout();
} catch (Exception e) {
Slog.w(TAG, "onCopySplashScreenComplete fail: " + this);
mStartingWindow.cancelAnimation();
parcelable.clearIfNeeded();
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
}
}
android/app/servertransaction/TransferSplashScreenViewStateItem.java
public void execute(@NonNull ClientTransactionHandler client,
@NonNull ActivityThread.ActivityClientRecord r,
@NonNull PendingTransactionActions pendingActions) {
client.handleAttachSplashScreenView(r, mSplashScreenViewParcelable, mStartingWindowLeash);
}
android/app/ActivityThread.java
public void handleAttachSplashScreenView(@NonNull ActivityClientRecord r,
@Nullable SplashScreenView.SplashScreenViewParcelable parcelable,
@NonNull SurfaceControl startingWindowLeash) {
final DecorView decorView = (DecorView) r.window.peekDecorView();
if (parcelable != null && decorView != null) {
// 将parcel和splashscreen的leash传进去
createSplashScreen(r, decorView, parcelable, startingWindowLeash);
} else {
// shouldn't happen!
Slog.e(TAG, "handleAttachSplashScreenView failed, unable to attach");
}
}
private void createSplashScreen(ActivityClientRecord r, DecorView decorView,
SplashScreenView.SplashScreenViewParcelable parcelable,
@NonNull SurfaceControl startingWindowLeash) {
final SplashScreenView.Builder builder = new SplashScreenView.Builder(r.activity);
//根据parcel构造View并且加到decorView
final SplashScreenView view = builder.createFromParcel(parcelable).build();
view.attachHostWindow(r.window);
decorView.addView(view);
view.requestLayout();
//在第一帧显示之前执行
view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
private boolean mHandled = false;
@Override
public boolean onPreDraw() {
if (mHandled) {
return true;
}
mHandled = true;
// Transfer the splash screen view from shell to client.
// Call syncTransferSplashscreenViewTransaction at the first onPreDraw, so we can
// ensure the client view is ready to show, and can use applyTransactionOnDraw to
// make all transitions happen at the same frame.
//同步处理startingWindowLeash和当前应用的第一帧,放到同一个事物
syncTransferSplashscreenViewTransaction(
view, r.token, decorView, startingWindowLeash);
view.post(() -> view.getViewTreeObserver().removeOnPreDrawListener(this));
return true;
}
});
}
private void syncTransferSplashscreenViewTransaction(SplashScreenView view, IBinder token,
View decorView, @NonNull SurfaceControl startingWindowLeash) {
// Ensure splash screen view is shown before remove the splash screen window.
// Once the copied splash screen view is onDrawn on decor view, use applyTransactionOnDraw
// to ensure the transfer of surface view and hide starting window are happen at the same
// frame.
final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
//将startwindow的leash隐藏
transaction.hide(startingWindowLeash);
// 执行事务
decorView.getViewRootImpl().applyTransactionOnDraw(transaction);
view.syncTransferSurfaceOnDraw();
// Tell server we can remove the starting window
decorView.postOnAnimation(() -> reportSplashscreenViewShown(token, view));
}
android/view/ViewRootImpl.java
public boolean applyTransactionOnDraw(@NonNull SurfaceControl.Transaction t) {
// mRemoved为false, 硬件加速也是开启的,走else
if (mRemoved || !isHardwareEnabled()) {
logAndTrace("applyTransactionOnDraw applyImmediately");
t.apply();
} else {
Trace.instant(Trace.TRACE_TAG_VIEW, "applyTransactionOnDraw-" + mTag);
// Copy and clear the passed in transaction for thread safety. The new transaction is
// accessed on the render thread.
// 将事务merge到mPendingTransaction,等下一帧一起提交
mPendingTransaction.merge(t);
mHasPendingTransactions = true;
}
return true;
}
事务的同步处理是很重要的