问题
在慢慢熟悉android 的过程中,发现一个view 或者layout的初始化,或者构造的流程还是比较清楚的,也就是加到父控件中,然后就开始了对应的生命周期。但是整个界面的父控件,或者说系统的第一个view, 是怎么来的,如何初始化和绘制的呢?
概述
概述:带着困扰我的问题,在前文的基础上,继续分析应用界面和framework的关系,通过分析viewrootimpl 的来源,并结合dialog, popupwindow, 和activity 的 根view的创建流程,回答了问题界面的根view 或者第一个view 是如何初始化,并加入到framework 中的。
分析viewrootimpl 的来源
本文分析是接上篇《源码分析:LayoutParams的wrap_content, match_parent, 和具体值》,在上文中简要分析了windowmanager中对界面的处理。
使用各种搜索方法,可以看到,全部android代码中只有一处引用ViewRootImpl 这个类,那就是
android.view.WindowManagerImpl.addView(View, LayoutParams, CompatibilityInfoHolder, boolean)
通过分析代码可以看到,windowmanager在addview() 的过程中,为了管理添加进来的view,使用了三个数组
private View[] mViews;
private ViewRootImpl[] mRoots;
private WindowManager.LayoutParams[] mParams;
在代码处理中,通过view 的context 来构造一个对应的ViewRootImpl ,然后把view, rootViewImpl, 和layoutParams 三个变量,存到数组中。并在最后,setView。
root = new ViewRootImpl(view.getContext());
root.mAddNesting = 1;
if (cih == null) {
root.mCompatibilityInfo = new CompatibilityInfoHolder();
} else {
root.mCompatibilityInfo = cih;
}
view.setLayoutParams(wparams);
if (mViews == null) {
index = 1;
mViews = new View[1];
mRoots = new ViewRootImpl[1];
mParams = new WindowManager.LayoutParams[1];
} else {
index = mViews.length + 1;
Object[] old = mViews;
mViews = new View[index];
System.arraycopy(old, 0, mViews, 0, index-1);
old = mRoots;
mRoots = new ViewRootImpl[index];
System.arraycopy(old, 0, mRoots, 0, index-1);
old = mParams;
mParams = new WindowManager.LayoutParams[index];
System.arraycopy(old, 0, mParams, 0, index-1);
}
index--;
mViews[index] = view;
mRoots[index] = root;
mParams[index] = wparams;
// do this last because it fires off messages to start doing things
root.setView(view, wparams, panelParentView);
在setview中,就是前文measure 过程中提出的问题, view是哪里来的-参见-《
尽量理解xml 中LayoutParams的wrap_content, match_parent, 和具体值》
好,现在知道了,rootview ,最终处理的view就是就是从windowmanager 中add进来的。那么顺藤摸瓜,看看到底哪里调用了WindowManagerImpl.addView()
ViewRootImpl(Context) - android.view.ViewRootImpl
addView(View, LayoutParams, CompatibilityInfoHolder, boolean) : void - android.view.WindowManagerImpl
addView(View, LayoutParams, CompatibilityInfoHolder) : void - android.view.WindowManagerImpl
addView(View, LayoutParams) : void - android.view.WindowManagerImpl
addIntruderView() : void - com.android.systemui.statusbar.phone.PhoneStatusBar
addNavigationBar() : void - com.android.systemui.statusbar.phone.PhoneStatusBar
addPanelWindows() : void - com.android.systemui.statusbar.tablet.TabletStatusBar (5 matches)
addStartingWindow(IBinder, String, int, CompatibilityInfo, CharSequence, int, int, int) : View - com.android.internal.policy.impl.PhoneWindowManager
addView(View) : void - android.view.WindowManagerImpl
advance() : void - com.android.systemui.statusbar.tablet.TabletTicker
handleResumeActivity(IBinder, boolean, boolean) : void - android.app.ActivityThread
handleShow() : void - android.widget.Toast.TN
invokePopup(LayoutParams) : void - android.widget.PopupWindow
makeVisible() : void - android.app.Activity
onBarViewAttached() : void - com.android.systemui.statusbar.phone.PhoneStatusBar
onCreate() : void - com.android.systemui.LoadAverageService
openPanel(PanelFeatureState, KeyEvent) : void - com.android.internal.policy.impl.PhoneWindow
setVisible(boolean) : void - android.widget.ZoomButtonsController
show() : void - android.app.Dialog
show() : void - com.android.internal.policy.impl.KeyguardViewManager
show(int) : void - android.widget.MediaController
showCompatibilityHelp() : void - com.android.systemui.statusbar.tablet.TabletStatusBar
showSafeModeOverlay() : void - com.android.server.am.ActivityManagerService
start() : void - com.android.systemui.statusbar.StatusBar
startAnimation(Runnable, int, int, boolean, boolean) : void - com.android.systemui.screenshot.GlobalScreenshot
updateRecentsPanel() : void - com.android.systemui.statusbar.phone.PhoneStatusBar
updateSettings() : void - com.android.internal.policy.impl.PhoneWindowManager
通过查看调用,可以看到很多地方都有调用。大概过一下,就能发现很多熟悉的东西,比如PhoneStatusBar,ActivityThread,PhoneWindow,PopupWindow,Activity,Toast,Dialog 等等。那这里感觉就比较明显了,这些熟悉的控件和类,就是通过windowmanager ,来把自己的view和界面加到系统中了。
分析Dialog 是如何加入到windowmanager 的
柿子先捡软的捏, 挑个简单的先。看下show() : void - android.app.Dialog
这不就是经常调用的大名鼎鼎的show() 嘛。 dialog写完最后调用的show() 方法,没错就是它。从这个控件来看确实就是这样通过windowmanager, show 一个dialog,就是windowmanager.addview一下啦。 那么addview的时候,自然就应该是dialog的根view,父控件喽。查看一下代码验证一下我们的猜测。
/**
* Start the dialog and display it on screen. The window is placed in the
* application layer and opaque. Note that you should not override this
* method to do initialization when the dialog is shown, instead implement
* that in {@link #onStart}.
*/
public void show() {
if (mShowing) {
if (mDecor != null) {
if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
}
mDecor.setVisibility(View.VISIBLE);
}
return;
}
mCanceled = false;
if (!mCreated) {
dispatchOnCreate(null);
}
onStart();
mDecor = mWindow.getDecorView();
if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
mActionBar = new ActionBarImpl(this);
}
WindowManager.LayoutParams l = mWindow.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
nl.copyFrom(l);
nl.softInputMode |=
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
l = nl;
}
try {
mWindowManager.addView(mDecor, l);
mShowing = true;
sendShowMessage();
} finally {
}
}
被add进 windowmanager中的是这个mDecor, 来自mWindow.getDecorView();
/**
* Retrieve the top-level window decor view (containing the standard
* window frame/decorations and the client's content inside of that), which
* can be added as a window to the window manager.
*
* <p><em>Note that calling this function for the first time "locks in"
* various window characteristics as described in
* {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}.</em></p>
*
* @return Returns the top-level window decor view.
*/
public abstract View getDecorView();
从注释中的描述,确实是 top-level window decor view。 印证了前面的猜测。
接下来看下这个mDecor到底是什么。
找到具体的实现com.android.internal.policy.impl.PhoneWindow.getDecorView()
@Override
public final View getDecorView() {
if (mDecor == null) {
installDecor();
}
return mDecor;
}
调用了installDecor()
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
}
<span style="white-space:pre"> </span>//......
}
继续generateDecor();
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
public DecorView(Context context, int featureId) {
super(context);
mFeatureId = featureId;
}
/** The feature ID of the panel, or -1 if this is the application's DecorView */
private final int mFeatureId;
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker
最后,从类定义中看到,DecorView 是一个 FrameLayout 并实现了RootViewSurfaceTaker
所以,回顾一下,这个 top-level window decor view,被add 进windowmanager 中的就是一个framelayout。
这点通过 hierarchyviewer 分析 UI 时得到印证。
如何使用,请移步官方文档《Using Hierarchy Viewer》,已经有很多人写了中文文档,不再赘述。
好了,到这里知道了系统dialog 中对应的 顶层 view 是一个framelayout, 对应到framework中 viewrootimpl 中的rootview。
同理,PopupWindow 是怎么干的
然后举一反三,看下PopupWindow 是怎么搞的。
invokePopup(LayoutParams) : void - android.widget.PopupWindow
showAsDropDown(View, int, int) : void - android.widget.PopupWindow
showAtLocation(IBinder, int, int, int) : void - android.widget.PopupWindow
这个接口showAsDropDown就是PopupWindow 的api嘛,
这里简单的列出关键代码,就不一一分析了。
mWindowManager.addView(mPopupView, p);
if (mBackground != null) {
final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
int height = ViewGroup.LayoutParams.MATCH_PARENT;
if (layoutParams != null &&
layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
height = ViewGroup.LayoutParams.WRAP_CONTENT;
}
// when a background is available, we embed the content view
// within another view that owns the background drawable
PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, height
);
popupViewContainer.setBackgroundDrawable(mBackground);
popupViewContainer.addView(mContentView, listParams);
mPopupView = popupViewContainer;
} else {
mPopupView = mContentView;
public void setContentView(View contentView) {
if (isShowing()) {
return;
}
mContentView = contentView;
if (mContext == null && mContentView != null) {
mContext = mContentView.getContext();
}
if (mWindowManager == null && mContentView != null) {
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
}
}
所以PopupWindow 是把setContentView 的view 加入到了windowmanager 中。
接下来是activity
那最常用的activity 中又是怎样的呢?
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
这里的mDecor 就不像前面的dialog 和popupWindow 中那么容易分析了。
首先扫了一下activity 这个类,发现没有赋值语句, 查看一下基类,也没有。最后查看所有mDecor 的调用。
mDecor - android.app.Activity
dispatchKeyEvent(KeyEvent) : boolean - android.app.Activity
handleDestroyActivity(IBinder, boolean, int, boolean) : void - android.app.ActivityThread (2 matches)
handleResumeActivity(IBinder, boolean, boolean) : void - android.app.ActivityThread (2 matches)
handleSendResult(ResultData) : void - android.app.ActivityThread
handleWindowVisibility(IBinder, boolean) : void - android.app.ActivityThread
makeVisible() : void - android.app.Activity (2 matches)
onWindowAttributesChanged(LayoutParams) : void - android.app.Activity
setVisible(boolean) : void - android.app.Activity
updateVisibility(ActivityClientRecord, boolean) : void - android.app.ActivityThread
逐个查看,最后仅在handleResumeActivity() : void - android.app.ActivityThread 中 找到相关赋值。
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
if (localLOGV) Slog.v(
TAG, "Resume " + r + " started activity: " +
a.mStartedActivity + ", hideForNow: " + r.hideForNow
+ ", finished: " + a.mFinished);
final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
<span style="color:#ff0000;">View decor = r.window.getDecorView();</span>
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
<span style="color:#ff0000;">a.mDecor = decor;</span>
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
<span style="white-space:pre"> </span>//......
}
这里代码涉及较多内容,集中注意力,看decorView ,注意到decor = r.window.getDecorView();。
而ActivityClientRecord r = performResumeActivity(token, clearHide);
也就是说是具体resume 的activity 的 window 的decorView()。
通过前面dialog 部分的分析,我们知道使用phoneWindow 的话,那就是framelayout 的那个 decorView。而目前还没有其他类型的window,这从window的继承关系可以看出。
在这里我们看到,activity 在resume的时候,通过判断是否要visible ,来把activity 的window 的decorView 加到windowmanager中,那么在activity的生命周期就应该在对应的状态中从windowmanager中移除该decorView。
在前面mDecor 的调用中,确实发现了在android.app.ActivityThread.handleDestroyActivity(IBinder, boolean, int, boolean),中。
private void handleDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance) {
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance);
if (r != null) {
cleanUpPendingRemoveWindows(r);
WindowManager wm = r.activity.getWindowManager();
<span style="color:#ff0000;">View v = r.activity.mDecor;</span>
if (v != null) {
if (r.activity.mVisibleFromServer) {
mNumVisibleActivities--;
}
IBinder wtoken = v.getWindowToken();
if (r.activity.mWindowAdded) {
if (r.onlyLocalRequest) {
// Hold off on removing this until the new activity's
// window is being added.
r.mPendingRemoveWindow = v;
r.mPendingRemoveWindowManager = wm;
} else {
<span style="color:#ff0000;"> wm.removeViewImmediate(v);</span>
}
}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>//......
}
至此,通过看了dialog, popupwindow, 和activity 中的部分源码, 知道了普通界面(dialog, popupwindow, 和activity )的第一个view, 是怎么来的,是如何加入到系统中的,也就是windowManager。
一句话结论
应用层面的界面都是通过windowmanager 加入到framework 中的,ViewRootImpl 是framework对view 的抽象, 界面管理的根节点。留的尾巴
弄清楚第一个的问题后,接下来争取比较完整地回顾一下view 的创建,绘制,layout过程。to be continued...
在分析过程中,简单的接触到了framework中处理activity的部分流程,handleResumeActivity(),handleDestroyActivity()。另一个问题就是系统framework 是如何管理activity的生命周期的。//TODO
在分析过程中,简单的接触到了framework中处理activity的部分流程,handleResumeActivity(),handleDestroyActivity()。另一个问题就是系统framework 是如何管理activity的生命周期的。//TODO