StartingWindow 自定义动画方案流程

116 阅读4分钟

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;
    }

事务的同步处理是很重要的