上期回顾
看完上篇文章 Android Espresso是如何获取View? (一)—— Espresso源码篇 我们知道了Espresso通过隐藏的API android.view.WindowManagerGlobal#getInstance().mViews 来获取所有的RootView,但是Android源码WindowManagerGlobal
这部分是如何实现的呢?
本文基于 AOSP android-7.1.2_r28 的代码进行分析,虽然和后续Android源码稍微有些出入,但是基本一致。
Dialog
我们先从简单的入手,AlertDialog是如何将RootView添加到window,显示View的。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAlert.installContent();
}
我们看到AlertDialog 在onCreate生命周期的时候初始化了ContentView
,调用了AlertController
的installContent
方法。
public void installContent() {
int contentView = selectContentView();
mWindow.setContentView(contentView);
setupView();
}
从Window class的注释
The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
我们可以了解到,PhoneWindow
是唯一的实现类,我们看看PhoneWindow
的setContentView
实现方法
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
// 初始化DecorView,这个就是我们之前一直提到的RootView
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
...
}
最后调用Dialog#show()
方法显示
public void show() {
...
// 将DecorView显示到到Window
mWindowManager.addView(mDecor, l);
mShowing = true;
sendShowMessage();
}
这个mWindowManager
获取方式就是大家耳熟能详的mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
,那他对应的是哪一个Service呢?我们看看WINDOW_SERVICE注册的地方
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx);
}});
原来他的实现类是WindowManagerImpl
,我们直接看看他的addView
方法
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
这个mGlobal
是啥呢?我们定睛一看
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
卧槽,终于看到了上篇文章中熟悉的WindowManagerGlobal
,到这里我们离真相只有一步之遥了,最后我们看看WindowManagerGlobal#addView
方法
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
synchronized (mLock) {
....
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
....
}
果然如上篇文章描述,最后将DecorView (RootView) 添加到 mViews
集合中去。
Activity
我们一般设置是通过Activity#setContentView
设置Activity的view
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
实际上还是调用了Window#setContentView
,上文中已经解析,这里不在描述。那View是啥时候被添加到window的呢?
我们知道Activity
的onResume
生命周期开始处理view,且Activity
生命周期都是由UI线程触发,UI线程又是在ActivityThread
中初始化和分发消息,这块代码我们不是我们这次解析的重点,我们直接看Activity
的onResume
生命周期处理逻辑
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
...
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
}
最后也是通过WindowManager
将view添加到window中,后面的逻辑上文已经解析了,大家应该都知道了,我在这里不就在重复了。
总结
看上去是很简单的过程,但是要在庞大的Android源码中抽丝剥茧出我们想要的东西还是有点难度的。
引申
俗话说学以致用,通过阅读源码我们知道了这个获取所有RootView的过程.但是这个有什么用呢?我们实际的开发过程中会有用到的地方吗?其实这个还是很有用的,比如Android的全埋点方案、APP的截图都需要获取所有RootView。
同学们有没有其他的场景可以使用到呢?欢迎在评论区畅所欲言!