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)!