Android14 - WindowManagerService之客户端Activity布局

2024-04-22 13:12

本文主要是介绍Android14 - WindowManagerService之客户端Activity布局,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Android14 - WindowManagerService之客户端Activity布局

主要角色

WMS作为一个服务端,有多种客户端与其交互的场景。我们以常见的Activity为例

ActivityActivityThread构建一个Activity调用attach方法attach构建一个PhoneWindow

PhoneWindow一个Activity对应一个PhoneWindow每个PhoneWindow持有一个WindowManagerImpl对象

WindowManagerImpl服务端WMS客户端代理WindowManagerImpl持有WindowManagerGlobal

WindowManagerGlobal进程单例WindowManagerGlobal不仅持有WMS服务端的WindowManagerService、WindowSession的引用并且集合所有ViewRootImplDecorView

ViewRootImpl每个Activity对应一个ViewRootImp对象ViewRootImp作为客户端与WMS桥梁。从客户端到服务端的角度,ViewRootImp持有WindowSession对象服务端Session引用,一个Session对应一个window服务端客户端角度ViewRootImp有一个子类Wbinder通信服务端W对象引用WMS持有用来服务端客户端通信另外ViewRootImp持有DecorView对象逻辑上ViewRootImplView本身并不是一个View只是实现ViewParent接口DecorViewViewRootImp子类(见setView()方法中的view.assignParent(this)

DecorView每个Activity对应一个DecorViewDecorView真正顶层View,是最外层的view其inflate PhoneWidow过来layout,将其addView作为自己的第0个类DecorView维护一个窗口基本结构包括内容区域titlebar区域

窗口的构建过程

窗口结构大致如下

其过程参考上图,在PhoneWindow初始化过程调用generateLayout(DecorView decor)方法其中一个window整体画面进行初始化

protected ViewGroup generateLayout(DecorView decor) {// Apply data from current theme.// Style来自于Theme xml文件TypedArray a = getWindowStyle();	mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)& (~getForcedWindowFlags());if (mIsFloating) {setLayout(WRAP_CONTENT, WRAP_CONTENT);setFlags(0, flagsToUpdate);} else {setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);getAttributes().setFitInsetsSides(0);getAttributes().setFitInsetsTypes(0);}if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {requestFeature(FEATURE_NO_TITLE);} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {// Don't allow an action bar if there is no title.requestFeature(FEATURE_ACTION_BAR);}if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {requestFeature(FEATURE_ACTION_BAR_OVERLAY);}if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {requestFeature(FEATURE_ACTION_MODE_OVERLAY);}if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowTranslucentStatus,false)) {setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS& (~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation,false)) {setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION& (~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));}if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch,getContext().getApplicationInfo().targetSdkVersion>= android.os.Build.VERSION_CODES.HONEYCOMB)) {setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));}	a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
	a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);if (DEBUG) Log.d(TAG, "Min width minor: " + mMinWidthMinor.coerceToString()+ ", major: " + mMinWidthMajor.coerceToString());if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) {if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
		a.getValue(R.styleable.Window_windowFixedWidthMajor,
				mFixedWidthMajor);}if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) {if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
		a.getValue(R.styleable.Window_windowFixedWidthMinor,
				mFixedWidthMinor);}if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) {if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
		a.getValue(R.styleable.Window_windowFixedHeightMajor,
				mFixedHeightMajor);}if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) {if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
		a.getValue(R.styleable.Window_windowFixedHeightMinor,
				mFixedHeightMinor);}if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) {requestFeature(FEATURE_CONTENT_TRANSITIONS);}if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) {requestFeature(FEATURE_ACTIVITY_TRANSITIONS);}	mIsTranslucent = a.getBoolean(R.styleable.Window_windowIsTranslucent, false);final Context context = getContext();final int targetSdk = context.getApplicationInfo().targetSdkVersion;final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;final boolean targetPreQ = targetSdk < Build.VERSION_CODES.Q;if (!mForcedStatusBarColor) {
		mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, Color.BLACK);}if (!mForcedNavigationBarColor) {final int navBarCompatibleColor = context.getColor(R.color.navigation_bar_compatible);final int navBarDefaultColor = context.getColor(R.color.navigation_bar_default);final int navBarColor = a.getColor(R.styleable.Window_navigationBarColor,
				navBarDefaultColor);		mNavigationBarColor =
				navBarColor == navBarDefaultColor&& !context.getResources().getBoolean(R.bool.config_navBarDefaultTransparent)? navBarCompatibleColor: navBarColor;		mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,Color.TRANSPARENT);}if (!targetPreQ) {
		mEnsureStatusBarContrastWhenTransparent = a.getBoolean(R.styleable.Window_enforceStatusBarContrast, false);
		mEnsureNavigationBarContrastWhenTransparent = a.getBoolean(R.styleable.Window_enforceNavigationBarContrast, true);}WindowManager.LayoutParams params = getAttributes();// Non-floating windows on high end devices must put up decor beneath the system bars and// therefore must know about visibility changes of those.if (!mIsFloating) {if (!targetPreL && a.getBoolean(R.styleable.Window_windowDrawsSystemBarBackgrounds,false)) {setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
					FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());}if (mDecor.mForceWindowDrawsBarBackgrounds) {
			params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;}
		params.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;}if (a.getBoolean(R.styleable.Window_windowNoMoveAnimation,false)) {
		params.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;}final int sysUiVis = decor.getSystemUiVisibility();final int statusLightFlag = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;final int statusFlag = a.getBoolean(R.styleable.Window_windowLightStatusBar, false)? statusLightFlag : 0;final int navLightFlag = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;final int navFlag = a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)? navLightFlag : 0;
	decor.setSystemUiVisibility((sysUiVis & ~(statusLightFlag | navLightFlag)) | (statusFlag | navFlag));if (a.hasValue(R.styleable.Window_windowLayoutInDisplayCutoutMode)) {int mode = a.getInt(R.styleable.Window_windowLayoutInDisplayCutoutMode, -1);if (mode < LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT|| mode > LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {throw new UnsupportedOperationException("Unknown windowLayoutInDisplayCutoutMode: "+ a.getString(R.styleable.Window_windowLayoutInDisplayCutoutMode));}
		params.layoutInDisplayCutoutMode = mode;}if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion>= android.os.Build.VERSION_CODES.HONEYCOMB) {if (a.getBoolean(R.styleable.Window_windowCloseOnTouchOutside,false)) {setCloseOnTouchOutsideIfNotSet(true);}}if (!hasSoftInputMode()) {
		params.softInputMode = a.getInt(R.styleable.Window_windowSoftInputMode,
				params.softInputMode);}if (a.getBoolean(R.styleable.Window_backgroundDimEnabled,
			mIsFloating)) {/* All dialogs should have the window dimmed */if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {
			params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;}if (!haveDimAmount()) {
			params.dimAmount = a.getFloat(android.R.styleable.Window_backgroundDimAmount, 0.5f);}}if (a.getBoolean(R.styleable.Window_windowBlurBehindEnabled, false)) {if ((getForcedWindowFlags() & WindowManager.LayoutParams.FLAG_BLUR_BEHIND) == 0) {
			params.flags |= WindowManager.LayoutParams.FLAG_BLUR_BEHIND;}		params.setBlurBehindRadius(a.getDimensionPixelSize(android.R.styleable.Window_windowBlurBehindRadius, 0));}setBackgroundBlurRadius(a.getDimensionPixelSize(R.styleable.Window_windowBackgroundBlurRadius, 0));if (params.windowAnimations == 0) {
		params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0);}// The rest are only done if this window is not embedded; otherwise,// the values are inherited from our container.if (getContainer() == null) {if (mBackgroundDrawable == null) {if (mFrameResource == 0) {
				mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);}if (a.hasValue(R.styleable.Window_windowBackground)) {
				mBackgroundDrawable = a.getDrawable(R.styleable.Window_windowBackground);}}if (a.hasValue(R.styleable.Window_windowBackgroundFallback)) {
			mBackgroundFallbackDrawable =
					a.getDrawable(R.styleable.Window_windowBackgroundFallback);}if (mLoadElevation) {
			mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);}
		mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
		mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);}// Inflate the window decor.// 根据Feature的设置,选择不同的layout xml。具体的resource来源于platform/frameworks/base/core/res/res,根据不同平台选择具体的资源文件。// 如果什么都没有设置,最后默认的会选择R.layout.screen_simple;int layoutResource;int features = getLocalFeatures();// System.out.println("Features: 0x" + Integer.toHexString(features));if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {if (mIsFloating) {TypedValue res = new TypedValue();getContext().getTheme().resolveAttribute(R.attr.dialogTitleIconsDecorLayout, res, true);
			layoutResource = res.resourceId;} else {
			layoutResource = R.layout.screen_title_icons;}// XXX Remove this once action bar supports these features.removeFeature(FEATURE_ACTION_BAR);// System.out.println("Title Icons!");} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {// Special case for a window with only a progress bar (and title).// XXX Need to have a no-title version of embedded windows.
		layoutResource = R.layout.screen_progress;// System.out.println("Progress!");} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {// Special case for a window with a custom title.// If the window is floating, we need a dialog layoutif (mIsFloating) {TypedValue res = new TypedValue();getContext().getTheme().resolveAttribute(R.attr.dialogCustomTitleDecorLayout, res, true);
			layoutResource = res.resourceId;} else {
			layoutResource = R.layout.screen_custom_title;}// XXX Remove this once action bar supports these features.removeFeature(FEATURE_ACTION_BAR);} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {// If no other features and not embedded, only need a title.// If the window is floating, we need a dialog layoutif (mIsFloating) {TypedValue res = new TypedValue();getContext().getTheme().resolveAttribute(R.attr.dialogTitleDecorLayout, res, true);
			layoutResource = res.resourceId;} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
			layoutResource = a.getResourceId(R.styleable.Window_windowActionBarFullscreenDecorLayout,R.layout.screen_action_bar);} else {
			layoutResource = R.layout.screen_title;}// System.out.println("Title!");} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
		layoutResource = R.layout.screen_simple_overlay_action_mode;} else {// Embedded, so no decoration is needed.
		layoutResource = R.layout.screen_simple;// System.out.println("Simple!");}	mDecor.startChanging();// 将最终的layoutResource传给DecorView,在DecorView里面去inflate
	mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);if (contentParent == null) {throw new RuntimeException("Window couldn't find content container view");}if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {ProgressBar progress = getCircularProgressBar(false);if (progress != null) {
			progress.setIndeterminate(true);}}// Remaining setup -- of background and title -- that only applies// to top-level windows.if (getContainer() == null) {
		mDecor.setWindowBackground(mBackgroundDrawable);final Drawable frame;if (mFrameResource != 0) {
			frame = getContext().getDrawable(mFrameResource);} else {
			frame = null;}
		mDecor.setWindowFrame(frame);		mDecor.setElevation(mElevation);
		mDecor.setClipToOutline(mClipToOutline);if (mTitle != null) {setTitle(mTitle);}if (mTitleColor == 0) {
			mTitleColor = mTextColor;}setTitleColor(mTitleColor);}	mDecor.finishChanging();return contentParent;
}

方法做了几件事

1. 获取主题属性TypedArray a = getWindowStyle();

getWindowStyle最终调用

platform/frameworks/base/core/java/android/content/Context.java@NonNull
public final TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[] attrs) {return getTheme().obtainStyledAttributes(attrs);
}

Theme获取根据不同平台、版本返回不同的文件:

platform/frameworks/base/core/java/android/content/res/Resources.java@UnsupportedAppUsage
public static int selectDefaultTheme(int curTheme, int targetSdkVersion) {return selectSystemTheme(curTheme, targetSdkVersion,com.android.internal.R.style.Theme,com.android.internal.R.style.Theme_Holo,com.android.internal.R.style.Theme_DeviceDefault,com.android.internal.R.style.Theme_DeviceDefault_Light_DarkActionBar);
}/** @hide */
public static int selectSystemTheme(int curTheme, int targetSdkVersion, int orig, int holo,int dark, int deviceDefault) {if (curTheme != ID_NULL) {return curTheme;}if (targetSdkVersion < Build.VERSION_CODES.HONEYCOMB) {return orig;}if (targetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {return holo;}if (targetSdkVersion < Build.VERSION_CODES.N) {return dark;}return deviceDefault;
}

对应platform/frameworks/base/core/res/res/values/themes.xmlplatform/frameworks/base/core/res/res/values/themes_holo.xml、platform/frameworks/base/core/res/res/values/themes_device_defaults.xml等这些主题文件

2. 根据theme配置设置featureflags背景

3. 根据设置后的feature选择一个layout文件layoutResourcelayoutResource传给DecorView进行窗口View构建。

一般layout分为上面的titlebar区域下面content区域titlebar部分区域不是固定每个layout可能不同布局content一般固定就是大部分layout一个id为contentlayout

默认platform/frameworks/base/core/res/res/layout/screen_simple.xml文件为例

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical"><ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" /><FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

action_mode_bar_stub是title部分

@android:id/content主内容区域。对应PhoneWindowmContentParent成员

整个布局对应DecorViewmContentRoot变量,在mContentRoot构建后,也被add到DecorView的第0个Child。具体

platform/frameworks/base/core/java/com/android/internal/policy/DecorView.javavoid onResourcesLoaded(LayoutInflater inflater, int layoutResource) {if (mBackdropFrameRenderer != null) {loadBackgroundDrawablesIfNeeded();
		mBackdropFrameRenderer.onResourcesLoaded(this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
				mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),getCurrentColor(mNavigationColorViewState));}	mDecorCaptionView = createDecorCaptionView(inflater);final View root = inflater.inflate(layoutResource, null);if (mDecorCaptionView != null) {if (mDecorCaptionView.getParent() == null) {addView(mDecorCaptionView,new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));}
		mDecorCaptionView.addView(root,new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));} else {// Put it below the color views.addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));}
	mContentRoot = (ViewGroup) root;initializeElevation();
}

这篇关于Android14 - WindowManagerService之客户端Activity布局的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/925870

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Java Websocket实例【服务端与客户端实现全双工通讯】

Java Websocket实例【服务端与客户端实现全双工通讯】 现很多网站为了实现即时通讯,所用的技术都是轮询(polling)。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发 出HTTP request,然后由服务器返回最新的数据给客服端的浏览器。这种传统的HTTP request 的模式带来很明显的缺点 – 浏 览器需要不断的向服务器发出请求,然而HTTP

lvgl8.3.6 控件垂直布局 label控件在image控件的下方显示

在使用 LVGL 8.3.6 创建一个垂直布局,其中 label 控件位于 image 控件下方,你可以使用 lv_obj_set_flex_flow 来设置布局为垂直,并确保 label 控件在 image 控件后添加。这里是如何步骤性地实现它的一个基本示例: 创建父容器:首先创建一个容器对象,该对象将作为布局的基础。设置容器为垂直布局:使用 lv_obj_set_flex_flow 设置容器

Apache Tiles 布局管理器

陈科肇 =========== 1.简介 一个免费的开源模板框架现代Java应用程序。  基于该复合图案它是建立以简化的用户界面的开发。 对于复杂的网站,它仍然最简单,最优雅的方式来一起工作的任何MVC技术。 Tiles允许作者定义页面片段可被组装成在运行一个完整的网页。  这些片段,或Tiles,可以用于为了降低公共页面元素的重复,简单地包括或嵌入在其它瓦片,制定了一系列可重复使用

【CSS in Depth 2 精译_023】第四章概述 + 4.1 Flexbox 布局的基本原理

当前内容所在位置(可进入专栏查看其他译好的章节内容) 第一章 层叠、优先级与继承(已完结) 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位(已完结) 2.1 相对单位的威力2.2 em 与 rem2.3 告别像素思维2.4 视口的相对单位2.5 无单位的数值与行高2.6 自定义属性2.7 本章小结 第三章 文档流与盒模型(已

ConstraintLayout布局里的一个属性app:layout_constraintDimensionRatio

ConstraintLayout 这是一个约束布局,可以尽可能的减少布局的嵌套。有一个属性特别好用,可以用来动态限制宽或者高app:layout_constraintDimensionRatio 关于app:layout_constraintDimensionRatio参数 app:layout_constraintDimensionRatio=“h,1:1” 表示高度height是动态变化

Redis 客户端Jedis使用---连接池

Jedis 是Redis 的Java客户端,通过一段时间的使用,jedis基本实现redis的所有功能,并且jedis在客户端实现redis数据分片功能,Redis本身是没有数据分布功能。 一、下载jedis 代码 jedis 代码地址:https://github.com/xetorthio/jedis 再次感受到开源的强大。呵呵,大家有时间可以看看源码。 二、项目中如何使用Jedi

html记账本改写:数据重新布局,更好用了,没有localStorage保存版本

<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>htm记账本</title><style>table {user-select: none;/* width: 100%; */border-collapse: collapse;}table,th,td {border: 1px solid bla

Java Socket服务器端与客户端的编程步骤总结

一,InetAddress类: InetAddress类没有构造方法,所以不能直接new出一个对象; 可以通过InetAddress类的静态方法获得InetAddress的对象; InetAddress.getLocalHost(); InetAddress.getByName(""); 类主要方法: String - address.getHostName(); String - addre

9.7(UDP局域网多客户端聊天室)

服务器端 #include<myhead.h>#define SERIP "192.168.0.132"#define SERPORT 8888#define MAX 50//定义用户结构体typedef struct{struct sockaddr_in addr;int flag;}User;User users[MAX];//用户列表void add_user(struct s