StartingWindow显示流程 - 安卓R

本文详细解析了Android系统中StartingWindow的工作机制,包括STARTING_WINDOW_TYPE_SNAPSHOT与STARTING_WINDOW_TYPE_SPLASH_SCREEN两种类型的区别及实现流程。通过跟踪源码,介绍了不同类型的StartingWindow如何创建、显示以及涉及的主要类和方法。

接着ATMS启动Activity流程中frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java的startActivityLocked方法中调用了frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java的showStartingWindow方法:

    void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) {
        ......
        final boolean shown = addStartingWindow(packageName, theme,
                compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
                allowTaskSnapshot(),
                mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal());
        if (shown) {
            mStartingWindowState = STARTING_WINDOW_SHOWN;
        }
    }

调用了ActivityRecord的addStartingWindow方法:

    boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
            CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
            IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
            boolean allowTaskSnapshot, boolean activityCreated) {
        ......
        final ActivityManager.TaskSnapshot snapshot =
                mWmService.mTaskSnapshotController.getSnapshot(task.mTaskId, task.mUserId,
                        false /* restoreFromDisk */, false /* isLowResolution */);
        final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
                allowTaskSnapshot, activityCreated, snapshot);

        if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
            ......
            return createSnapshot(snapshot);
        }
        ......
        // There is no existing starting window, and we don't want to create a splash screen, so
        // that's it!
        if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
            return false;
        }

        ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SplashScreenStartingData");
        mStartingData = new SplashScreenStartingData(mWmService, pkg,
                theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                getMergedOverrideConfiguration());
        scheduleAddStartingWindow();
        return true;
    }

这里的type主要有两种情况,分别是STARTING_WINDOW_TYPE_SNAPSHOTSTARTING_WINDOW_TYPE_SPLASH_SCREEN

如果是STARTING_WINDOW_TYPE_SNAPSHOT,则调用了ActivityRecord的createSnapshot方法:

    private boolean createSnapshot(ActivityManager.TaskSnapshot snapshot) {
        ......
        mStartingData = new SnapshotStartingData(mWmService, snapshot);
        scheduleAddStartingWindow();
        return true;
    }

创建了SnapshotStartingData对象mStartingData,最后调用了ActivityRecord的scheduleAddStartingWindow方法。

回到ActivityRecord的addStartingWindow方法中,如果是STARTING_WINDOW_TYPE_SPLASH_SCREEN,则创建了SplashScreenStartingData对象mStartingData,最后也调用了ActivityRecord的scheduleAddStartingWindow方法:

    void scheduleAddStartingWindow() {
        // Note: we really want to do sendMessageAtFrontOfQueue() because we
        // want to process the message ASAP, before any other queued
        // messages.
        if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
            ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Enqueueing ADD_STARTING");
            mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
        }
    }

通过mWmService.mAnimationHandler.postAtFrontOfQueue切换到动画线程调用了ActivityRecord的成员变量mAddStartingWindow的run方法:

    private class AddStartingWindow implements Runnable {

        @Override
        public void run() {
            ......
            WindowManagerPolicy.StartingSurface surface = null;
            try {
                surface = startingData.createStartingSurface(ActivityRecord.this);
            } catch (Exception e) {
                Slog.w(TAG, "Exception when adding starting window", e);
            }
            ......
        }
    }

这里调用了之前创建的startingData的createStartingSurface方法,如果是SnapshotStartingData,则调用了frameworks/base/services/core/java/com/android/server/wm/SnapshotStartingData.java的createStartingSurface方法:

    @Override
    StartingSurface createStartingSurface(ActivityRecord activity) {
        return mService.mTaskSnapshotController.createStartingSurface(activity, mSnapshot);
    }

调用了frameworks/base/services/core/java/com/android/server/wm/TaskSnapshotController.java的createStartingSurface方法:

    /**
     * Creates a starting surface for {@param token} with {@param snapshot}. DO NOT HOLD THE WINDOW
     * MANAGER LOCK WHEN CALLING THIS METHOD!
     */
    StartingSurface createStartingSurface(ActivityRecord activity,
            TaskSnapshot snapshot) {
        return TaskSnapshotSurface.create(mService, activity, snapshot);
    }

调用了frameworks/base/services/core/java/com/android/server/wm/TaskSnapshotSurface.java的create方法:

    static TaskSnapshotSurface create(WindowManagerService service, ActivityRecord activity,
            TaskSnapshot snapshot) {

        final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
        final Window window = new Window();
        final IWindowSession session = WindowManagerGlobal.getWindowSession();
        window.setSession(session);
        final SurfaceControl surfaceControl = new SurfaceControl();
        ......
        try {
            final int res = session.addToDisplay(window, window.mSeq, layoutParams,
                    View.GONE, activity.getDisplayContent().getDisplayId(), tmpFrame, tmpRect,
                    tmpRect, tmpCutout, null, mTmpInsetsState, mTempControls);
            if (res < 0) {
                Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
                return null;
            }
        } catch (RemoteException e) {
            // Local call.
        }
        final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
                surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis,
                windowFlags, windowPrivateFlags, taskBounds, currentOrientation, activityType,
                insetsState);
        window.setOuter(snapshotSurface);
        try {
            session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
                    tmpFrame, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect,
                    tmpCutout, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
                    mTempControls, sTmpSurfaceSize, sTmpSurfaceControl);
        } catch (RemoteException e) {
            // Local call.
        }

        final Rect systemBarInsets = getSystemBarInsets(tmpFrame, insetsState);
        snapshotSurface.setFrames(tmpFrame, systemBarInsets);
        snapshotSurface.drawSnapshot();
        return snapshotSurface;
    }

通过session.addToDisplay创建了显示窗口(此流程具体见View的添加流程),并创建了TaskSnapshotSurface对象,存放了显示窗口相关信息。

回到ActivityRecord的成员变量mAddStartingWindow的run方法中,如果startingData是SplashScreenStartingData,则调用了frameworks/base/services/core/java/com/android/server/wm/SplashScreenStartingData.java的createStartingSurface方法:

    @Override
    StartingSurface createStartingSurface(ActivityRecord activity) {
        return mService.mPolicy.addSplashScreen(activity.token, mPkg, mTheme, mCompatInfo,
                mNonLocalizedLabel, mLabelRes, mIcon, mLogo, mWindowFlags,
                mMergedOverrideConfiguration, activity.getDisplayContent().getDisplayId());
    }

调用了frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java的addSplashScreen方法:

    /** {@inheritDoc} */
    @Override
    public StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName,
            int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
            int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {
        ......

        WindowManager wm = null;
        View view = null;

        try {
            ......
            final PhoneWindow win = new PhoneWindow(context);
            win.setIsStartingWindow(true);
            ......
            params.setTitle("Splash Screen " + packageName);
            addSplashscreenContent(win, context);

            wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
            view = win.getDecorView();

            if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for "
                + packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null));

            wm.addView(view, params);

            // Only return the view if it was successfully added to the
            // window manager... which we can tell by it having a parent.
            return view.getParent() != null ? new SplashScreenSurface(view, appToken) : null;
        } catch (WindowManager.BadTokenException e) {
            // ignore
            Log.w(TAG, appToken + " already running, starting window not displayed. " +
                    e.getMessage());
        } catch (RuntimeException e) {
            // don't crash if something else bad happens, for example a
            // failure loading resources because we are loading from an app
            // on external storage that has been unmounted.
            Log.w(TAG, appToken + " failed creating starting window", e);
        } finally {
            if (view != null && view.getParent() == null) {
                Log.w(TAG, "view not successfully added to wm, removing view");
                wm.removeViewImmediate(view);
            }
        }

        return null;
    }

通过wm.addView创建显示窗口(此流程具体见View的添加流程),并创建了SplashScreenSurface对象,存放了显示窗口相关信息。

StartingWindow后面是怎样销毁的见StartingWindow销毁流程

总结

1 StartingWindow分为2种,分别是STARTING_WINDOW_TYPE_SNAPSHOT和STARTING_WINDOW_TYPE_SPLASH_SCREEN。

2 STARTING_WINDOW_TYPE_SNAPSHOT一般显示的是Activity之前的一张截图,如果Activity是secure的,则会显示的内容根据其theme绘制。

3 STARTING_WINDOW_TYPE_SPLASH_SCREEN显示的内容根据Activity的theme绘制。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SSSxCCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值