自学内容网 自学内容网

Android11 SplashScreen 的显示和退出流程

应用的启动到显示到屏幕是需要一定的时间的,为了提升用户的体验,google加入了启动窗口,也就是SplashScreen

SplashScreen显示流程
在应用的启动过程中,会调用到ActivityStarter的startActivityInner方法,具体可参考:Android11 应用启动流程

ActivityStarter.startActivityInner

//frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
int startActivityInner(/*省略*/){
//省略
 mTargetStack.startActivityLocked(mStartActivity,
                topStack != null ? topStack.getTopNonFinishingActivity() : null, newTask,
                mKeepCurTransition, mOptions);
//省略
}

ActivityStack.startActivityLocked

//frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java
void startActivityLocked(/*省略*/) {
//省略
if (r.mLaunchTaskBehind) {
               //省略
} else if (SHOW_APP_STARTING_PREVIEW && doShow) {
               //省略
r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity));
}

//省略
}

ActivityRecord.showStartingWindow

//frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) {
        //省略
        final CompatibilityInfo compatInfo =
                mAtmService.compatibilityInfoForPackageLocked(info.applicationInfo);
        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;
        }
    }

addStartingWindow

//frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
boolean addStartingWindow(/*省略*/) {
if (theme != 0) {
            AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
                    com.android.internal.R.styleable.Window,
                    mWmService.mCurrentUserId);
          //开始获取配置的属性
            final boolean windowIsTranslucent = ent.array.getBoolean(
                    com.android.internal.R.styleable.Window_windowIsTranslucent, false);
            final boolean windowIsFloating = ent.array.getBoolean(
                    com.android.internal.R.styleable.Window_windowIsFloating, false);
            final boolean windowShowWallpaper = ent.array.getBoolean(
                    com.android.internal.R.styleable.Window_windowShowWallpaper, false);
            final boolean windowDisableStarting = ent.array.getBoolean(
                    com.android.internal.R.styleable.Window_windowDisablePreview, false);
           
            if (windowIsTranslucent) {//配置了windowIsTranslucent,直接返回
                return false;
            }
            if (windowIsFloating || windowDisableStarting) {//配置了windowDisablePreview或者windowIsFloating也返回
                return false;
            }
           //省略
mStartingData = new SplashScreenStartingData(mWmService, pkg,
                theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                getMergedOverrideConfiguration());
        scheduleAddStartingWindow();
        return true;

}

如果不想要这个启动窗口,就可以参考配置对应的属性。
创建SplashScreenStartingData,然后调用scheduleAddStartingWindow继续处理

scheduleAddStartingWindow

//frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
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);
        }
    }

private final AddStartingWindow mAddStartingWindow = new AddStartingWindow();

执行AddStartingWindow的run方法

private class AddStartingWindow implements Runnable {
@Override
    public void run() {
synchronized (mWmService.mGlobalLock) {
              //省略
        startingData = mStartingData;//对startingData 进行了赋值
        }
         WindowManagerPolicy.StartingSurface surface = null;
            try {
                surface = startingData.createStartingSurface(ActivityRecord.this);
            } catch (Exception e) {
                Slog.w(TAG, "Exception when adding starting window", e);
            }
}
if (surface != null) {
startingSurface = surface;//对startingSurface 赋值
}
//省略
}

SplashScreenStartingData.createStartingSurface

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

mService.mPolicy是PhoneWindowManager对象

PhoneWindowManager.addSplashScreen

//frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
 @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) {
if (!SHOW_SPLASH_SCREENS) {//不要启动窗口的话,也可以修改这个值
            return null;
        }
if (theme != context.getThemeResId() || labelRes != 0) {
                try {
                    context = context.createPackageContextAsUser(packageName, CONTEXT_RESTRICTED,
                            UserHandle.of(userId));//获取要启动应用的context
                    context.setTheme(theme);//设置主题
                } catch (PackageManager.NameNotFoundException e) {
                   
                }
       }
//省略
final PhoneWindow win = new PhoneWindow(context);//创建PhoneWindow
 win.setType(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);//设置type为TYPE_APPLICATION_STARTING
final WindowManager.LayoutParams params = win.getAttributes();
       params.token = appToken;//设置token
       params.packageName = packageName;//设置包名
       addSplashscreenContent(win, context);//可以配置启动窗口要显示的内容
       wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
       view = win.getDecorView();
wm.addView(view, params);//添加view
 
return view.getParent() != null ? new SplashScreenSurface(view, appToken) : null;
}

可以看出,SplashScreen的添加和系统窗口的添加是一样,都是调用addView去添加一个窗口。需要注意

  • 窗口类型为TYPE_APPLICATION_STARTING
  • token为ActivityRecord的token,在WMS端决定该窗口是挂在ActivityRecord下
  • 返回的是一个SplashScreenSurface,也就是说前面startingSurface 是一个SplashScreenSurface对象

最后来看一下addSplashscreenContent方法

//frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
private void addSplashscreenContent(PhoneWindow win, Context ctx) {
        final TypedArray a = ctx.obtainStyledAttributes(R.styleable.Window);
        final int resId = a.getResourceId(R.styleable.Window_windowSplashscreenContent, 0);
        a.recycle();
        if (resId == 0) {
            return;
        }
        final Drawable drawable = ctx.getDrawable(resId);
        if (drawable == null) {
            return;
        }

        // We wrap this into a view so the system insets get applied to the drawable.
        final View v = new View(ctx);
        v.setBackground(drawable);
        win.setContentView(v);
    }

通过配置windowSplashscreenContent来设置启动窗口需要显示的内容

SplashScreen退出流程
待启动的应用绘制完成之后,需要退出SplashScreen,其调用流程如下

WindowManager: at com.android.server.wm.ActivityRecord.removeStartingWindow(ActivityRecord.java:1970)
WindowManager: at com.android.server.wm.ActivityRecord.onFirstWindowDrawn(ActivityRecord.java:5346)
WindowManager: at com.android.server.wm.WindowState.performShowLocked(WindowState.java:4438)
WindowManager: at com.android.server.wm.WindowStateAnimator.commitFinishDrawingLocked(WindowStateAnimator.java:375)

从performShowLocked开始分析

//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
boolean performShowLocked() {
//省略

        final int drawState = mWinAnimator.mDrawState;
        if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW) && mActivityRecord != null) {
            if (mAttrs.type != TYPE_APPLICATION_STARTING) {
                mActivityRecord.onFirstWindowDrawn(this, mWinAnimator);//现在要显示的不是启动窗口
            } else {
                mActivityRecord.onStartingWindowDrawn();
            }
        }
//省略

ActivityRecord.onFirstWindowDrawn

//frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) {
       //省略
        removeStartingWindow();
//省略
    }

removeStartingWindow

//frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
void removeStartingWindow() {
//省略
final WindowManagerPolicy.StartingSurface surface;
if (mStartingData != null) {
surface = startingSurface;
mStartingData = null;
startingSurface = null;
startingWindow = null;
startingDisplayed = false;

//省略
 mWmService.mAnimationHandler.post(() -> {
            ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Removing startingView=%s", surface);
            try {
                surface.remove();
            } catch (Exception e) {
                Slog.w(TAG_WM, "Exception when removing starting window", e);
            }
});
}

首先对surface进行赋值并清空一些变量,startingSurface是前面通过createStartingSurface得到的SplashScreenSurface对象,然后调用SplashScreenSurface的remove方法

SplashScreenSurface.remove

 @Override
    public void remove() {
        final WindowManager wm = mView.getContext().getSystemService(WindowManager.class);
        wm.removeView(mView);
    }

调用removeView去移除之前显示的启动窗口。

总结
启动窗口的启动和退出也是通过addView/removeView来实现的(本文忽略了WMS端的处理)

启动
在这里插入图片描述
退出
在这里插入图片描述


原文地址:https://blog.csdn.net/littleyards/article/details/140378185

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!