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 {@Overridepublic 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

	@OverrideStartingSurface 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@Overridepublic 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));//获取要启动应用的contextcontext.setTheme(theme);//设置主题} catch (PackageManager.NameNotFoundException e) {}}//省略final PhoneWindow win = new PhoneWindow(context);//创建PhoneWindowwin.setType(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);//设置type为TYPE_APPLICATION_STARTINGfinal WindowManager.LayoutParams params = win.getAttributes();params.token = appToken;//设置tokenparams.packageName = packageName;//设置包名addSplashscreenContent(win, context);//可以配置启动窗口要显示的内容wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);view = win.getDecorView();wm.addView(view, params);//添加viewreturn 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

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/45472.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024 China Joy 前瞻 | 腾讯网易发新作,网易数智携游戏前沿科技、创新产品以及独家礼盒,精彩不断!

今年上半年&#xff0c;CES、MWC和AWE三大国际科技展轮番轰炸&#xff0c;吸引全球科技爱好者的高度关注&#xff0c;无论是新潮的科技产品&#xff0c;还是对人工智能的探索&#xff0c;每一项展出的技术和产品都引起了市场的热议。而到了下半年&#xff0c;一年一度的China J…

AGE 在CTE表达式中使用Cypher

在使用CTE&#xff08;公共表达式&#xff09;时&#xff0c;没有对使用Cypher的限制。 查询&#xff1a; WITH graph_query as (SELECT *FROM cypher(graph_name, $$MATCH (n)RETURN n.name, n.age$$) as (name agtype, age agtype) ) SELECT * FROM graph_query;结果&#…

【Linux】进程7——查看进程

1.为什么进程管理这么重要呢&#xff1f; 这是因为&#xff1a; 首先&#xff0c;我们在操作系统时的各项任务其实都是经过某个PID来完成的&#xff08;包括你的bash环境&#xff09;&#xff0c;因此&#xff0c;能不能执行某项任务&#xff0c;就与该进程的权限有关了。再来…

服务端生成RSA密钥实例

RSA非对称加密算法的一种&#xff0c;这里分享一下服务端生成公钥和私钥的实例&#xff0c;并打印出来。 一&#xff1a;实例代码 package mainimport ("bufio""crypto/rand""crypto/rsa""crypto/x509""encoding/pem"&quo…

centos切换python默认版本的最简单步骤(随手记)

centos8默认安装的python版本是python3.6&#xff0c;当需要更高版本的python3.9或3.11时&#xff0c;我们经常搜索到源码安装然后编写软连接。 [rootmanager Python-3.9.0]# python3 --version Python 3.6.8 [rootmanager Python-3.9.0]# pip3 --version pip 9.0.3 from /usr…

RAG理论:ES混合搜索BM25+kNN(cosine)以及归一化

接前一篇:RAG实践:ES混合搜索BM25+kNN(cosine) https://blog.csdn.net/Xin_101/article/details/140230948 本文主要讲解混合搜索相关理论以及计算推导过程, 包括BM25、kNN以及ES中使用混合搜索分数计算过程。 详细讲解: (1)ES中如何通过BM25计算关键词搜索分数; (2)…

Linux系统探索

本文不定时更新linux及类linux系统的有趣玩法和知识 一、有趣玩法 1. xshell连linux调试台时打印个性化信息 要在每次通过 xshell 连接到 Linux 调试台时打印个性化信息&#xff0c;可以通过修改 SSH 配置文件来实现。以下是具体步骤&#xff1a; 登录到 Linux 调试台&#…

SpringBoot注解--11--@JSONField @JsonProperty

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一个问题&#xff1a;后端实体类isXXX开头的属性&#xff0c;传到前端后自动去掉is解决方法&#xff1a; JsonProperty和JSONField1.简介2.注解的区别2.1 底层框架不…

容联云发布容犀大模型应用,重塑企业“营销服”|WAIC 2024

7月6日&#xff0c;在2024世界人工智能大会上&#xff0c;容联云成功举办主题为“数智聚合 产业向上”的生成式应用与大模型商业化实践论坛。 论坛上&#xff0c;容联云发布了容犀智能大模型应用升级&#xff0c;该系列应用包括容犀Agent Copilot、容犀Knowledge Copilot、容犀…

【区分vue2和vue3下的element UI Collapse 折叠面板组件,分别详细介绍属性,事件,方法如何使用,并举例】

在 Vue 2 中&#xff0c;我们通常使用 Element UI&#xff0c;而在 Vue 3 中&#xff0c;我们则使用 Element Plus 来获得类似的组件库功能。对于 Collapse 折叠面板组件&#xff0c;这两个库提供了相似的接口&#xff0c;但具体属性、事件和方法可能略有不同。以下将分别介绍 …

python库(12):Requests库实现HTTP请求

1 Requests库 Requests是一个极为流行的HTTP库&#xff0c;它允许你发送各种类型的HTTP请求。无论是GET、POST、PUT、DELETE还是其他类型的请求&#xff0c;Requests都能轻松搞定。 虽然还有其他HTTP库&#xff0c;但Requests库以其简洁的API和人性化的设计脱颖而出。它隐藏了…

Springboot 配置 log4j2 时的注意事项

感谢博主 https://www.cnblogs.com/fishlittle/p/17950944 依赖 SpringBoot 的 spring-boot-starter/ spring-boot-starter-web 自带的是 logback 日志&#xff0c;若要使用 log4j2 日志&#xff0c;需要引入对应依赖。logback 日志和 log4j2 日志都是对 slf4j 门面的实现&am…

寻找赛灵思IP核手册的办法

一、先google找到大家看的手册都是什么样的 二、在AMD技术手册网站搜索手册的名称 网址&#xff1a;AMD Technical Information Portal

数据湖仓一体(六)安装flink

上传安装包到/opt/software目录并解压 [bigdata@node106 software]$ tar -zxvf flink-1.17.2-bin-scala_2.12.tgz -C /opt/services/ 重命名文件 [bigdata@node106 services]$ mv flink-1.17.2-bin-scala_2.12 flink-1.17.2 配置环境变量 [bigdata@node106 ~]$ sudo vim /…

数据守卫者:sklearn中的异常点检测技术

数据守卫者&#xff1a;sklearn中的异常点检测技术 在数据分析和机器学习中&#xff0c;异常点&#xff08;也称为离群点&#xff09;是指那些不符合数据集其余部分的模式或行为的点。异常点检测是识别这些异常值的过程&#xff0c;它对于确保数据质量和提高模型性能至关重要。…

Docker 安装字体文件

由于 Docker 容器的隔离性&#xff0c;与宿主机是独立的运行环境&#xff0c;如果需要用到宿主机的字体文件就需要进行安装。 例如在导出 PDF 文件时&#xff0c;如果缺少字体文件&#xff0c;就会产生乱码&#xff08;常表现为中文变成方框&#xff09;。 Docker 字体文件的安…

国密证书(gmssl)在Kylin Server V10下安装

1.查看操作系统信息 [root@localhost ~]# cat /etc/.kyinfo [dist] name=Kylin milestone=Server-V10-GFB-Release-ZF9_01-2204-Build03 arch=arm64 beta=False time=2023-01-09 11:04:36 dist_id=Kylin-Server-V10-GFB-Release-ZF9_01-2204-Build03-arm64-2023-01-09 11:04:…

科普文:spring boot中常用的接口、工具栏、注解整理

1.springboot 常用接口 1.1 Aware接口 Spring IOC容器中 Bean是感知不到容器的存在&#xff0c;Aware(意识到的)接口就是帮助Bean感知到IOC容器的存在&#xff0c;即获取当前Bean对应的Spring的一些组件&#xff0c;如当前Bean对应的ApplicationContext等。 1.1.1 Applicati…

python如何进行pip换源

hello&#xff0c;大家好&#xff0c;我是一名测试开发工程师&#xff0c;至今已在自动化测试领域深耕9个年头&#xff0c;现已将本人实战多年的多终端自动化测试框架【wyTest】开源啦&#xff0c;请大家快来体验并关注我吧。 Python的包管理工具pip是开发者必备的利器之一。然…

企业公司网站建站自适应网站源码系统 前后端分离 带完整的源代码包以及搭建部署教程

系统概述 在数字化转型的浪潮中&#xff0c;企业网站已成为展示品牌形象、吸引潜在客户、促进业务交流的重要窗口。为了满足企业对于高效、灵活、易维护网站建设的迫切需求&#xff0c;小编给大家分享一款集先进技术与人性化设计于一体的“企业公司网站建站自适应网站源码系统…