背景
在对 Activity 的启动分析中,我们看到了 Activity 是如何和 Window 产生关联的
//ActivityThread 中的方法
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
...
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
//这里的 a 就是 Activity,即 r.activity
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
//和UI部分挂钩了,非常重要的一个步骤,涉及到和wms、sf之间的通信。
wm.addView(decor, l);
}
...
}
如上所示,本质上 Activity 中布局,是通过 WindowManger 的 addView 设置进去的。下面我们就来具体分析一下这个过程。在此之前,我们先用一张图看一下它们之间的整体包含关系与通信,有一个大体上的了解。
Window 的创建及作用
Window 的创建,是在 Activity 中的 attach 进行的。
//Activity 中的方法
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) {
//注意,这就是为什么 attach 之后才可以调用 context 的方法
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
//创建 Window
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
......
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);
}
由上可知,Activity 中创建了 Window,并且每个 Activity 都有一个与之对应的Window ,具体的实现类,其实是一个 PhoneWindow。在 Android之setContentView 这篇文章中,我们分析了 PhoneWindow 负责对整个布局的管理,它提供了一个模板,并把我们的布局 set 进这个模板中id 为 content 的控件中。
上图中的 DecorView 是在 PhoneWindow 中生成的,也就是最终显示给用户的 UI。回到背景中介绍的,系统最终将它从 Window 中取出,并通过 WindowManager 进行了 add 操作。那么,接下来,先不急着看 addView 方法,我们先来认识一下这里的 WindowManager。
我们看到,Window 会为自己 set 一个 WindowManager,而这个 WindowManager,实际上,是
一个 WindowManagerImpl,从上面代码块中,我们可以看到它的构建过程
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
//Window 中的方法
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
......
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
//WindowMangerImpl 中的方法
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
即,通过getSystemService(Context.WINDOW_SERVICE) 方法得到的,WindowManagerImpl中的方法,是交给其内部的单例 WindowManagerGlobal 实现的。每个应用进程只有一个WindowManagerGlobal,而每个 Activity 或者说每个 Window
都会有一个 WindowManagerImpl。现在,我们将焦点聚焦在了 WinodowManagerGlobal 的 addView 方法上。
addView 方法
Ok, 现在我们可以来看看 addView 方法了。
//WindowManagerGlobal.java文件中
//保存了每个 Window 所对应的 view,也就是 decorVIew
private final ArrayList<View> mViews = new ArrayList<View>();
//保存了每个 Window 所对应的 ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
//保存了每个 Window 所对应的布局参数
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
private final ArraySet<View> mDyingViews = new ArraySet<View>();
//这里的 view 实际上是 decorView
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
......
ViewRootImpl root;
View panelParentView = null;
......
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
......
root.setView(view, wparams, panelParentView);
}
WindowManagerGlobal 中维护这一些数据结构。其中,每个 Window 对应一个 ViewRootImpl。而addView 实际上将调用 ViewRootImpl 的 setView 方法
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
......
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
......
}
包含关系是:WindowManagerImpl 存在成员变量 WindowManagerGlobal; WindownManagerGlobal 包含成员变量 ViewRootImpl;ViewRootImpl 包含成员变量 IWindowSession。
IWindowSession 虽然是 ViewRootImpl 的成员变量,是实际上是在 ViewManagerGlobal 的方法中实例化的。
//ViewManagerGlobal 中的方法
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
由上可知,每个进程的 Session 也是唯一的。IWindowSession 是一个接口, 实际上是 WindowManager 调用 openSession 之后返回的是一个实现类 Session。这个返回过程实际上是一个跨进程调用的过程。每次 getWindowSession 返回的都是服务端同一个 Session。
//Session 中的方法
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
//这个 mService 是 WindowManagerService
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
追究其本质,现在又回到了 WindowManagerService 中。它的 addWindow 又是什么呢?
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
......
//Window 不能重复添加
if (mWindowMap.containsKey(client.asBinder())) {
Slog.w(TAG_WM, "Window " + client + " is already added");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
//如果是子窗口,取值在 FIRST_SUB_WINDOW 和 LAST_SUB_WINDOW 之间
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
attachedWindow = windowForClientLocked(null, attrs.token, false);
//子窗口必须附着在一个 Window 上
if (attachedWindow == null) {
Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
//子窗口所附着的窗口不能是一个子窗口
if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {
Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
......
WindowState win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
......
//接下来,就是 WMS 向 SurfaceFlinger 申请 Surface,然后传回给 Activity 使用的
win.attach();
......
小结
本节到此为止,也算是对以前所写的 activity 启动分析、setContentView 分析等相关内容的一个串联。WMS 和 SurfaceFlinger 之间,是如何申请 Surface、又是如何回传和 App 的内容,目前还没有展开;还有就是这其中事件的传递机制是如何展开的,这两部分可以作为后续章节。
参考链接:
http://gityuan.com/2017/04/16/activity-with-window/
https://www.jianshu.com/p/40776c123adb
http://blog.csdn.net/yhaolpz/article/details/68936932