二、Activity的Window创建与关联View过程
1. Window创建过程
在 ActivityThread 中的performLaunchActivity
函数中, 先创建了Activity,然后调用了Activity的attach函数。
-> ActivityThread .java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
}
}
...
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);
...
return activity;
}
再来看一下Activity
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
AutofillManager.AutofillClient {
// 成员变量
private Window mWindow;
private WindowManager mWindowManager;
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) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
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();
...
}
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
...
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
}
public final class WindowManagerImpl implements WindowManager {
...
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
...
}
Window是一个抽象类,它的具体实现是PhoneWindow。从代码中可以看出 mWindow 实例化为 PhoneWindow,并为 mWindow设置多个回调Callback(参数都传入this,因为Activity实现了相应的接口)。WindowManager是一个接口,WindowManagerImpl 是它的实现类,mWindowManager 实例化为 WindowManagerImpl。
2. DecorView添加到Window中
在 ActivityThread 中的handleResumeActivity
函数中, 先调用Activity的onResume方法。然后会获取Activity的Window(PhoneWindow)的DecorView,再调用Activity的makeVisible。在makeVisible中,DecorView被添加到WindowManager中。
-> ActivityThread .java
final void handleResumeActivity(...) {
...
r = performResumeActivity(token, clearHide, reason);
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
a.mDecor = decor;
...
r.activity.makeVisible();
...
}
-> PhoneWindow.java
public final View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
}
-> Activity.java
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
在
getDecorView
中,如果mDecor不为null的情况才会直接返回,那么在什么情况下mDecor才不为null呢?
如果在Activity的onCreate中调用了setContentView
设置Activity的布局,那么最终会进行mDecor的创建和初始化,但是此时的DecorView还没有被WindowManager正是添加到Window中。详解请参考:三、setContentView加载布局源码分析(DecorView的创建)
如果在Activity中并没有调用setContentView
创建DecorView,getDecorView()会调用installDecor()创建DecorView。
其实setContentView的过程也调用了这个installDecor()
方法。所以有没有调用setContentView创建DecorView的过程,本质上是一样的。不一样的是setContentView还在DecorView的mContentParent中添加了自己的布局
。
既然拿到了DecorView,那么接下来我们就重点分析:
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
从上面的描述可知,Activity的WindowManager的具体实现就是WindowManagerImpl。那我们直接看WindowManagerImpl的addView
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 以单例模式
向外提供自己的实例。WindowManagerImpl 这种工作模式是典型的桥接模式
,将所有的操作全部委托给WindowManagerGlobal 来实现。
-> WindowManagerGlobal.java
/** WindowManagerGlobal比较重要的成员变量,均是列表*/
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
private final ArraySet<View> mDyingViews = new ArraySet<View>();
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
/** 1. 检查参数是否合法,如果是子Window那么还需要调整一些布局参数*/
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
}
...
/** 2. 创建 ViewRootImpl并将View添加到列表上*/
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
/** 3. View的绘制过程是由 ViewRootImpl来完成的,
* 在 setView内部会通过 requestLayout来完成异步刷新请求。
*/
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
addView中,主要做了三个任务(已在代码中注释)。接下来我们就来深入ViewRootImpl的setView方法。
-> ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
...
}
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
requestLayout();//绘制View
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
}
}
在ViewRootImpl的setView函数中,先调用了requestLayout来绘制view,然后调用了mWindowSession(通过WindowManagerGlobal创建)的addToDisplay函数和WMS(全称为WindowManagerService)通信。
requestLayout涉及到View的绘制过程,在这里就不详讲。
-> WindowManagerGlobal.java
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;
}
}
这里的getWindowManagerService
是通过AIDL
返回WindowManagerService
实例。之后调用WindowManagerService的openSession()。继续跟踪下去
-> WindowManagerService.java
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
返回一个Session
对象。也就是说在ViewRootImpl的setView()中调用的是Session的addToDisplay()
。继续跟踪下去
-> com.android.server.wm.Session.java
final WindowManagerService mService;
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
也就是说最后调用的是WindowManagerService的addWindow
-> WindowManagerService.java
final WindowHashMap mWindowMap = new WindowHashMap();
public int addWindow(...) {
...
WindowState win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
win.attach();
mWindowMap.put(client.asBinder(), win);
...
}
将WindowManager添加进WindowManagerService统一管理。
至此,整个添加视图操作解析完毕。
WindowManagerService添加Window的过程就不详讲了,有兴趣的可以参考:
Activity WMS ViewRootImpl三者关系(Activity创建窗口 按键分发等)
这也解释了为什么说“在onCreate至onResume过程中,Activity已经对系统可见,但是还没有展示到界面上”的原因。这时因为Activity被装载而且已经执行完attach、onCreate、onStart、甚至执行完onResume,但是DecorView始终没有被添加到Window上。onCreate、onStart中获取不到控件宽高也是因为这个原因,在WindowManager#addView(内部实例化ViewRoot)之后才会真正的执行layout、measure、draw
。
参考:
Android 使用WindowManager实现悬浮窗及源码解析
Android 顶级视图DecorView的前世今生