Android11 SplashScreen 的显示和退出流程

news2024/9/22 21:33:01

应用的启动到显示到屏幕是需要一定的时间的,为了提升用户的体验,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端的处理)

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

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

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

相关文章

STM32HAL库+ESP8266+cJSON+微信小程序_连接华为云物联网平台

STM32HAL库ESP8266cJSON微信小程序_连接华为云物联网平台 实验使用资源&#xff1a;正点原子F407 USART1&#xff1a;PA9P、A10&#xff08;串口打印调试&#xff09; USART3&#xff1a;PB10、PB11&#xff08;WiFi模块&#xff09; DHT11&#xff1a;PG9&#xff08;采集数据…

【JavaEE精炼宝库】文件操作(2)——文件内容读写 | IO流

文章目录 一、输入流1.1 InputStream 概述&#xff1a;1.2 read 方法详解&#xff1a;1.3 close 方法&#xff1a;1.4 利用 Scanner 进行读操作&#xff1a;1.5 Reader&#xff1a; 二、输出流2.1 OutputStream 概述&#xff1a;2.2 write 方法详解&#xff1a;2.3 利用 PrintW…

【Sql Server】sql server 2019设置远程访问,外网服务器需要设置好安全组入方向规则

大家好&#xff0c;我是全栈小5&#xff0c;欢迎来到《小5讲堂》。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言1、无法链接…

47、lvs之DR

1、DR模式&#xff1a; 1.1、lvs三种模式&#xff1a; nat 地址转换 DR 直接路由模式 tun 隧道模式 1.2、DR模式的特点&#xff1a; 调度器在整个lvs集群当中是最重要的&#xff0c;在nat模式下&#xff0c;即负载接收请求&#xff0c;同时根据负载均衡的算法转发流量&…

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

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

springboot商城综合项目自动化系统-计算机毕业设计源码051018

摘 要 目前电商系统商城项目管理极其频繁,迫切地需要自动化测试来代替人工繁琐而又重复的劳动。自动化测试相关的研究已经很多,但多数只是针对某一方面,比如单一接口或者单一页面或者性能等,而缺乏将接口、页面、持续集成系统和缺陷管理系统整合的自动化测试平台。本研究采用混…

ROS——六轴机械臂建模

创建工作空间及目录&#xff08;my_arm&#xff09; 创建launch、urdf文件夹 urdf launch Arm_Model.xacro <?xml version"1.0"?> <robot name"marm" xmlns:xacro"http://www.ros.org/wiki/xacro"><!-- Defining the c…

【Python3】自动化测试_Playwright的安装教程

安装playwright库&#xff1a; pip install playwright 安装内置浏览器&#xff1a; # Install All playwright install# Install chromium playwright install chromium# Install firefox playwright install firefox# Install WebKit playwright install webkit 安装系统…

32.同步FIFO-IP核的调用

&#xff08;1&#xff09;FIFO&#xff08;First In First Out&#xff0c;即先进先出&#xff09;&#xff0c;是一种数据缓冲器&#xff0c;用来实现数据先入先出的读写方式。 &#xff08;2&#xff09;FIFO存储器主要是作为缓存&#xff0c;应用在同步时钟系统和异步时钟系…

深入解析发生 OOM 的三大场景

深入解析 OOM 的三大场景 什么是 OOM&#xff1f;一、堆内存溢出 ( Heap OOM )原因分析解决方案 二、栈内存溢出&#xff08;Stack OOM&#xff09;原因分析解决方案 三、方法区内存溢出&#xff08;Metaspace OOM&#xff09;原因分析解决方案 在Java应用程序开发中&#xff0…

CORDIC Translate

随便记录一下下&#xff1a; Cordic IP核使用说明以及避坑记录-CSDN博客 本次只用到了Translate&#xff0c;记录一下自己遇到的坑坑 实际配置&#xff1a; timescale 1ns / 1nsmodule cordic_tb();reg clk;wire m_axis_dout_tvalid;reg s_axis_cartesian_tvalid 0;wire [31…

三分钟了解什么是消费返利

消费返利作为一种创新的营销手段&#xff0c;不仅增强了消费者的购物体验&#xff0c;还为电商平台及商家带来了更多的用户粘性和复购率。什么是消费返利&#xff0c;其背后的运作机制又是如何的呢&#xff1f; 返利的定义: 返利即是一种消费激励措施&#xff0c;当消费者在指…

网络安全防御 -- 防火墙安全策略用户认证综合实验

实验拓扑&#xff1a; 实验目的&#xff1a; 1、DMZ区内的服务器&#xff0c;办公区仅能在办公时间内(9:00-18:00)可以访问&#xff0c;生产区的设备全天可以访问。 2、生产区不允许访问互联网&#xff0c;办公区和游客区允许访问互联网。 3、办公区设备10.0.2.10不允许访问DM…

ns3-gym入门(一):代码结构和简单例子

ns3-gym真的好难学&#xff0c;网上可以参考的例子也太少了&#xff0c;如果有用这个做路由的麻烦联系我交流一下吧&#xff0c;太痛苦了 一、安装 之前的文章已经提到过了&#xff0c;这里不赘述了 二、运行简单的例子 用两个终端的方式运行感觉更加直观&#xff0c;但是仍然…

白嫖3个域名用cloudeflare托管

这也是看到别的up主分享得到的&#xff0c;我也分享一下给你们。 浏览器打开官网 https://register.us.kg/ 然后注册 根据信息填地址电话可以去另一个网页生成&#xff0c;名字姓名之类的可以填自己喜欢的&#xff0c;注意一下&#xff0c;我尝试了一下谷歌的邮箱&#xff0…

webstorm问题解决:无法识别 @

问题解决tsconfig.json 问题 本地的 vite.config.ts 已经配置 路径 但是&#xff0c;我用webstorm 上识别不了 解决 新增文件tsconfig.json&#xff0c;添加 baseUrl 和 paths 的配置&#xff0c;以告诉 TypeScript 和 WebStorm 如何解析路径别名 tsconfig.json {&quo…

以设备为核心的状态自动采集、人工运维和预测性维护为一体的智能管理系统

中服云设备全生命周期管理系统充分利用物联网、人工智能、机器学习、大数据等新一代技术&#xff0c;实现对企业生产设备从采购、安装、调试、使用、维护、维修、改造、更新直到报废全生命周期的智能化、数字化、可视化的实时管控&#xff0c;支持设备运行状态的自动采集和人工…

SketchUp Pro 2023: 强大的跨平台建模软件

SketchUp Pro 2023 是一款广受建筑设计师、工程师和创意设计师欢迎的专业建模软件&#xff0c;它结合了直观的用户界面和强大的功能&#xff0c;为用户提供了创造、设计和分享他们的想法的理想平台。无论是初学者还是经验丰富的专业人士&#xff0c;SketchUp Pro 2023 都能够满…

JVM是如何管理内存的?图文详解GC垃圾回收算法

前言&#xff1a;在C/C中对于变量的内存空间一般都是由程序员手动进行管理的&#xff0c;往往会伴随着大量的 malloc 和 free 操作&#xff0c;常常会有很多问题困扰开发者&#xff0c;这个代码会不会发生内存泄漏&#xff1f;会不会重复释放内存&#xff1f;但是在Java开发中我…

python作业二

# 二进制转化为十进制 num input("num:")def binaryToDecimal(binaryString):he 0length len(binaryString)for i in range(length):he int(binaryString[i]) * 2 ** (length - i - 1)return heprint(binaryToDecimal(num))代码运行如下&#xff1a; import math…