Activity
和View
是大家都比较熟悉一个是Android的四大组件、一个是用于展示各种控件的View
,相对于前两者 Window
在日常开发中比较陌生,今天我们这篇文章就将这三个问题讲清楚。
源代码版本:
要想弄清楚 Window
对象是什么使用创建的,就要从最简单的代码入手,一步步得往下查看源码才能解开这个迷惑,那我们就开始吧
首先看以下代码:
class MyTestViewActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my_test_view)
//打印View所以父View
LayoutUtils.printAllViewLayout(ll_first_content)
}
}
activity_my_test_view.xml 布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/ll_first_content"
android:orientation="vertical"/>
这里我们有一个 继承与 AppCompatActivity
的 MyTestViewActivity
里面只是调用了 setContentView()
将自定义布局文件传入其中,在 onCreate()
我们调用 printAllViewLayout()
将 自定义布局 LinearLayout
的所有父View 打印出来。
printAllViewLayout()
如下:
fun printAllViewLayout(view: View?) {
if (view == null) {
return
}
//打印view所有的parent
var parent = view.parent
while (parent != null) {
Log.d("printAllViewLayout: ","parent : $parent")
parent = parent.parent
}
}
上述代码运行之后打印如下信息:
printAllViewLayout:: parent : androidx.appcompat.widget.ContentFrameLayout
printAllViewLayout:: parent : androidx.appcompat.widget.FitWindowsLinearLayout
printAllViewLayout:: parent : android.widget.FrameLayout
printAllViewLayout:: parent : android.widget.LinearLayout
printAllViewLayout:: parent : com.android.internal.policy.PhoneWindow$DecorView
printAllViewLayout:: parent : android.view.ViewRootImpl
这里我们看到打印出很多父View
类型、 ContentFrameLayout
、FitWindowsLinearLayout
、 FrameLayout
、 LinearLayout
以及 DecorView
和 ViewRootImpl
这些都是什么时候添加的呢?又起到什么作用呢?我们带着这两个问题从源码的角度进行一旦究竟吧
首先我们需要从 setContentView()
的具体实现开始分析
//AppCompatActivity 实现
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
//AppCompatDelegate 实现类 AppCompatDelegateImpl
@Override
public void setContentView(int resId) {
//创建DecorView
ensureSubDecor();
//加载自定义布局 并添加到 contentParent 中
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mOriginalWindowCallback.onContentChanged();
}
AppCompatActivity
中 setContentView()
是有其代理类实现,AppCompatDelegateImpl
类中 setContentView()
只做了两件事
AppCompatDelegateImpl
类中自定义的ViewGroup)我们继续往下看 ensureSubDecor()
具体是怎么实现的
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
mSubDecor = createSubDecor();
//省略不重要代码
}
}
ensureSubDecor()
中很显然只有一句比较重要的代码 调用了 createSubDecor()
而 mSubDecorInstalled 只是一个标志位用于标识是否已经初始化过了,当执行完 createSubDecor()
会置成 true 因此保证只会初始化一次。
我们接着往下看 createSubDecor()
具体做了什么
private ViewGroup createSubDecor() {
//省略不重要代码...
// 确保window已经加载了 DecorView 如果没有则进行创建并添加到window上
mWindow.getDecorView();
//根据不同的 theme 加载不同的layout 布局文件
final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;
//存在 windowTitle 情况下加载 带有actionBar 的布局文件
if (!mWindowNoTitle) {
if (mIsFloating) {
// If we're floating, inflate the dialog title decor
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_dialog_title_material, null);
// Floating windows can never have an action bar, reset the flags
mHasActionBar = mOverlayActionBar = false;
} else if (mHasActionBar) {
// Now inflate the view using the themed context and set it as the content view
subDecor = (ViewGroup) LayoutInflater.from(themedContext).inflate(R.layout.abc_screen_toolbar, null);
}
} else {
if (mOverlayActionMode) {
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple_overlay_action_mode, null);
} else {
//没有windowTitle 默认情况下加载此布局
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
}
}
// 将上述创建的自定义ViewGroup 添加到DecorView 中的 ContentParent 中
mWindow.setContentView(subDecor);
// 返回自定义ViewGroup
return subDecor;
}
上述createSubDecor()
只做了三件大事,具体是:
Window
中 getDecorView()
创建DecorView、创建 ContentParent ViewGroup 并加入到DecorView中Window
中setContentView()
将创建的ViewGroup 添加到 ContentParent 中接下来我们先一件一件拆开来分析,首先我们来看第一件事 也就是 getDecorView()
, Window
是一个抽象类,而 Window
的唯一实现类 是 com.android.internal.policy.PhoneWindow
所以我们就看一下 PhoneWindow
是怎么实现的。
PhoneWindow
的 getDecorView()
很显然这个方法是个单例的,只会创建一个 DecorView
对象,而且具体实现是在 installDecor()
中
@Override
public final @NonNull View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
}
//安装 DecorView 布局
private void installDecor() {
if (mDecor == null) {
//创建 DecorView
mDecor = generateDecor(-1);
}
if (mContentParent == null) {
//创建 ContentParent 布局
mContentParent = generateLayout(mDecor);
}
//省略无关代码
}
installDecor()
方法比较长,但实际只干了两件事:
generateDecor()
创建 DecorView 对象generateLayout()
创建ContentParent 对象generateDecor()
直接创建了一个新的 DecorView
对象
protected DecorView generateDecor(int featureId) {
// 省略不重要代码
//创建DecorView 并将当前Window对象传入
return new DecorView(context, featureId, this, getAttributes());
}
DecorView
其实就是继承 FrameLayout
的自定义 ViewGroup
、在构造器中调用 setWindow()
将当前 Window
进行了保存
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
DecorView(Context context, int featureId, PhoneWindow window,
WindowManager.LayoutParams params) {
super(context);
setWindow(window);
//省略不重要代码
}
//保存window对象
void setWindow(PhoneWindow phoneWindow) {
mWindow = phoneWindow;
Context context = getContext();
if (context instanceof DecorContext) {
DecorContext decorContext = (DecorContext) context;
decorContext.setPhoneWindow(mWindow);
}
}
generateLayout()
这个方法比较长、但其作用只有两个:
ContentParent
并加入到 DecorView
中protected ViewGroup generateLayout(DecorView decor) {
// 1.应用所有主题设置
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);
}
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);
}
//省略部分代码
// 2.加载window ContentView 布局 布局均为 LinearLayout 嵌套 FrameLayout
int layoutResource;
//省略layout布局文件匹配代码
//加载布局文件
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
return contentParent;
}
上述源码中针对 ContentParent
的布局文件进行了匹配、framework
层帮我们定制了很多ContentParent
布局文件,但这些布局文件只有两部分组成,Title部分 和 Content 部分、所以ContentView 布局 布局均为 LinearLayout 嵌套 FrameLayout ,默认情况下会加载的是 screen_simple.xml
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>
onResourcesLoaded()
中加载了布局文件,并添加到 DecorView 中成为DecorView中第一个ViewGroup
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
//加载布局文件
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 {
//将contentView加入到 DecorView中 成为DecorView中第一个ViewGroup
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
}
至此 PhonewWindow
层的 getDecorView()
实现我们已经分析完毕,我们再回到 AppCompat 层中看一下第二步,看看AppCompat层又做了那些功能。
还记得上文中 createSubDecor()
方法第二步做了什么吗?
private ViewGroup createSubDecor() {
//省略不重要代码...
// 确保window已经加载了 DecorView 如果没有则进行创建并添加到window上
mWindow.getDecorView();
//根据不同的 theme 加载不同的layout 布局文件
final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;
//存在 windowTitle 情况下加载 带有actionBar 的布局文件
if (!mWindowNoTitle) {
if (mIsFloating) {
// If we're floating, inflate the dialog title decor
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_dialog_title_material, null);
// Floating windows can never have an action bar, reset the flags
mHasActionBar = mOverlayActionBar = false;
} else if (mHasActionBar) {
// Now inflate the view using the themed context and set it as the content view
subDecor = (ViewGroup) LayoutInflater.from(themedContext).inflate(R.layout.abc_screen_toolbar, null);
}
} else {
if (mOverlayActionMode) {
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple_overlay_action_mode, null);
} else {
//没有windowTitle 默认情况下加载此布局
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
}
}
// 将上述创建的自定义ViewGroup 添加到DecorView 中的 ContentParent 中
mWindow.setContentView(subDecor);
// 返回自定义ViewGroup
return subDecor;
}
对 就是 AppCompat 层又增加了一层 ViewGroup
这里我的 Activity
已经去掉了Window Title
所以加载的是 abc_screen_simple.xml
布局文件
abc_screen_simple.xml
布局文件:
<androidx.appcompat.widget.FitWindowsLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/action_bar_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:fitsSystemWindows="true">
<androidx.appcompat.widget.ViewStubCompat
android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/abc_action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include layout="@layout/abc_screen_content_include" />
androidx.appcompat.widget.FitWindowsLinearLayout>
//abc_screen_content_include 布局文件
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.appcompat.widget.ContentFrameLayout
android:id="@id/action_bar_activity_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
merge>
可以看出 AppCompat 层帮我们添加了一层ViewGroup 帮我们实现了其他效果等
我们前面分析了 AppCompat 层 createSubDecor()
的前两步实现,我们现在看一下,最后一步
// 将上述创建的自定义ViewGroup 添加到DecorView 中的 ContentParent 中
mWindow.setContentView(subDecor);
看一下 PhoneWindow
是怎么实现的
@Override
public void setContentView(View view) {
setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
//创建 mContentParent
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
//将指定view添加到 mContentParent 中
mContentParent.addView(view, params);
}
}
可以看出 当 mContentParent == null
就调用 installDecor()
来创建 DecorView
、ContentParent
等逻辑 我们前文就已经分析了,最后将指定的View
添加到 ContentParent
中 (这里的view 就是刚才 AppCompat 层 创建的自定义ContentView)
至此 PhoneWindow
层 setContentView()
实现我们已经分析完毕,那么我们重新把视线转移到 AppCompat 层
我们重新回到 AppCompat 层 setContentView() 中继续往下分析
//AppCompatDelegate 实现类 AppCompatDelegateImpl
@Override
public void setContentView(int resId) {
//创建DecorView
ensureSubDecor();
//加载自定义布局 并添加到 contentParent 中
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mOriginalWindowCallback.onContentChanged();
}
这里直接将 resId
加载并添加到了contentParent
中, mSubDecor
就是 AppCompat 创建的自定义 ViewGroup
至此我们从 setContentView()
出发分析了 setContentView()
底层具体做了哪些操作,创建了哪些 View
分别是怎么实现的,又回到了这里明白了 setContentView()
调用过程,但是 setContentView()
只是创建了 DecorView
及其他ViewGroup
但这些 View
是怎么展示在 Activity
上的呢?
要想弄清楚 View
是如何展示在 Activity
上的,就首先弄清楚 Window
和 Activity
之间的关系
Activity
启动的时候,代码最终会执行 ActivityThread
中 performLaunchActivity()
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//省略代码...
ContextImpl appContext = createBaseContextForActivity(r);
//创建 activity 实例
Activity activity = null;
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
//省略代码...
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
//执行 attach() 并传入Activity所需参数
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);
//省略代码...
//执行 Activity onCreate()
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
}
//省略代码...
return activity;
}
在performLaunchActivity()
中 Instrumentation
会通过反射创建 Activity
对象,之后执行其 attach()
并传入Activity
所需参数,Window
对象就是在这个方法中创建的
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
//创建PhoneWindow 对象
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
//省略代码...
//将系统 WindowManager 传递到 PhoneWindow中
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
mWindow.setColorMode(info.colorMode);
//省略代码...
}
可以看到 在 attach()
中创建了 PhoneWindow
之后 调用 setWindowManager()
将系统 WindowManager
传递到 PhoneWindow
中
而我们知道 WindowManager
只是一个接口,所以真实“干活”的类 其实是 WindowManagerImpl
它实现了 WindowManager
,所以 PhoneWindow
其实持有的 WindowManager
对象是 WindowManagerImpl
//Window 实现
public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {
setWindowManager(wm, appToken, appName, false);
}
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
//创建 WindowManagerImpl 对象
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
//WindowManagerImpl 实现
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
这里我们知道 在创建 Activity
的时候并创建了 PhoneWindow
对象,但是并没有将 View
添加到 Activity
上,那 Window
什么时候将 View
添加到 Activity
上呢?
其实熟悉Android的开发者可能不陌生,在 Activity
执行完 onCreate()
这个时候界面其实是不可见的,只有等到 onResume()
阶段 PhoneWindow
才将 DecorView
绘制到Activity
上
在ActivityThread
的handleResumeActivity()
才调用 WindowManager
的 addView()
将 View 添加到 WMS(WindowManagerService)上,DecorView 被渲染绘制到屏幕上显示
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
//省略代码...
final Activity a = r.activity;
boolean willBeVisible = !a.mStartedActivity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
ViewManager wm = a.getWindowManager();
// 如果没有添加过 则将View 添加到 WMS 上
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
}
}
//省略代码...
}
PhoneWindow
只是负责处理一些应用窗口通用的逻辑(设置标题栏,导航栏等)。但是真正完成把一个 View
作为窗口添加到 WMS 的过程是由 WindowManager
来完成的。
上文我们分析过 WindowManager
真实实现类 是 WindowManagerImpl
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params)
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
}
很显然 WindowManagerImpl
将工作交给了一个 WindowManagerGlobal
去做,WindowManagerGlobal
是一个单例对象,看一下 WindowManagerGlobal
的 addView()
是怎么实现的
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
//省略代码...
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
//省略代码...
//创建 ViewRootImpl 对象
root = new ViewRootImpl(view.getContext(), display);
//调用 ViewRootImpl其 setView来添加view
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
throw e;
}
}
}
可见 WindowManagerGlobal
也不是真正实现者,这里 addView()
直接创建出 ViewRootImpl
对象并调用 setView()
将 View
添加到 WMS 中。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
//省略代码...
//开始View的绘制流程
requestLayout();
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
//将View 添加到 WMS 中
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
} catch (RemoteException e) {
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
//省略代码...
// 设置输入管道
CharSequence counterSuffix = attrs.getTitle();
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
}
}
ViewRootImpl
的 setView()
是一个比较重要的方法,但是这个方法比较长,我们挑重点代码进行分析,其实做了一下事情:
requestLayout()
开启View的绘制流程。 调用此方法后 ViewRootImpl
所关联的 View 也执行 measure - layout - draw 操作,确保在 View
被添加到 Window
上显示到屏幕之前,已经完成测量和绘制操作。IWindowSession
中 addToDisplay()
使用 Binder 通讯方式将 View
传递给 WMS我们这里只重点分析第二步,将 Window
显示在 屏幕上,这里我们看到 mWindowSession.addToDisplay()
、mWindowSession
对象也是 WindowManagerGlobal
中单例实现,初始化代码如下:
@UnsupportedAppUsage
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
// Emulate the legacy behavior. The global instance of InputMethodManager
@UnsupportedAppUsage
InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
});
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
返回的 IWindowSession
其实是一个 aidl 定义接口,其实现类 是 System 进程的 Session
(源码路径:com.android.server.wm.Session.java)
// IWindowSession.aidl
interface IWindowSession {
int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outFrame,
out Rect outContentInsets, out Rect outStableInsets, out Rect outOutsets,
out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
out InsetsState insetsState);
}
//com.android.server.wm.Session.java 部分源码
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowManagerService mService;
final IWindowSessionCallback mCallback;
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
outInsetsState);
}
}
Session
中的 mService
就是 WMS。至此,Window
已经成功的被传递给了 WMS。剩下的工作就全部转移到系统进程中的 WMS 来完成最终的添加操作。
至此,我们从源码的角度分析了 通过 setContentView()
的流程,分析了 Activity
、Window
、View
之间的关系。整个过程 Activity
表面上参与度比较低,大部分 View
的添加操作都由 Window
中实现。而 Activity
就相当于一个管理类,通过它能够更简单的实现 Window
和 View
的操作逻辑。
根据它们之间的关系可以绘制出以下示例图:
最后再简单列一下整个流程需要注意的点:
Activity
中有一个 Window
,也就是 PhoneWindow
对象,在 PhoneWindow
中会创建一个 DecorView
,在 调用setContentView()
的时候会将布局文件添加到此 DecorView
中。WindowManagerGlobal
对象PhoneWindow
对应一个 ViewRootImpl
对象WindowMangerGlobal
通过调用 ViewRootImpl
的 setView()
,完成 Window
的添加过程,这一步是使用 Binder
进行通讯的。ViewRootImpl
的 setView()
除了完成对Window
的添加还完成了 View
渲染(requestLayout()
)以及接收触屏事件。好了,从本文的学习 你是否明白了文章开头在 Activity
中 onCreate()
中我们打印出来的那些父View
,是什么时候添加进去的呢?
Android FrameWork Base 源代码:https://github.com/aosp-mirror/platform_frameworks_base
AndroidX release 源代码:https://android.googlesource.com/platform/frameworks/support/+refs
https://blog.csdn.net/lu1024188315/article/details/74911179