源码解析:dialog, popupwindow, 和activity 的第一个view是怎么来的?
2020-12-13 02:54
标签:源码 hierarchy 控件 界面 framework 在慢慢熟悉android 的过程中,发现一个view 或者layout的初始化,或者构造的流程还是比较清楚的,也就是加到父控件中,然后就开始了对应的生命周期。但是整个界面的父控件,或者说系统的第一个view, 是怎么来的,如何初始化和绘制的呢? 概述:带着困扰我的问题,在前文的基础上,继续分析应用界面和framework的关系,通过分析viewrootimpl 的来源,并结合dialog, popupwindow, 和activity 的 根view的创建流程,回答了问题界面的根view 或者第一个view 是如何初始化,并加入到framework 中的。 本文分析是接上篇《源码分析:LayoutParams的wrap_content, match_parent, 和具体值》,在上文中简要分析了windowmanager中对界面的处理。 使用各种搜索方法,可以看到,全部android代码中只有一处引用ViewRootImpl 这个类,那就是 android.view.WindowManagerImpl.addView(View, LayoutParams, CompatibilityInfoHolder, boolean) 通过分析代码可以看到,windowmanager在addview() 的过程中,为了管理添加进来的view,使用了三个数组 柿子先捡软的捏, 挑个简单的先。看下show() : void - android.app.Dialog 这不就是经常调用的大名鼎鼎的show() 嘛。 dialog写完最后调用的show() 方法,没错就是它。从这个控件来看确实就是这样通过windowmanager, show 一个dialog,就是windowmanager.addview一下啦。 那么addview的时候,自然就应该是dialog的根view,父控件喽。查看一下代码验证一下我们的猜测。 Note that calling this function for the first time "locks in"
* various window characteristics as described in
* {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}. 接下来看下这个mDecor到底是什么。 找到具体的实现com.android.internal.policy.impl.PhoneWindow.getDecorView() 所以,回顾一下,这个 top-level window decor view,被add 进windowmanager 中的就是一个framelayout。 这点通过 hierarchyviewer 分析 UI 时得到印证。 如何使用,请移步官方文档《Using Hierarchy Viewer》,已经有很多人写了中文文档,不再赘述。 好了,到这里知道了系统dialog 中对应的 顶层 view 是一个framelayout, 对应到framework中 viewrootimpl 中的rootview。 然后举一反三,看下PopupWindow 是怎么搞的。
那最常用的activity 中又是怎样的呢? 首先扫了一下activity 这个类,发现没有赋值语句, 查看一下基类,也没有。最后查看所有mDecor 的调用。 而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),中。
在分析过程中,简单的接触到了framework中处理activity的部分流程,handleResumeActivity(),handleDestroyActivity()。另一个问题就是系统framework 是如何管理activity的生命周期的。//TODO
在分析过程中,简单的接触到了framework中处理activity的部分流程,handleResumeActivity(),handleDestroyActivity()。另一个问题就是系统framework 是如何管理activity的生命周期的。//TODO 源码解析:dialog, popupwindow, 和activity 的第一个view是怎么来的?,搜素材,soscw.com 源码解析:dialog, popupwindow, 和activity 的第一个view是怎么来的? 标签:源码 hierarchy 控件 界面 framework 原文地址:http://blog.csdn.net/farmer_cc/article/details/30968459问题
概述
分析viewrootimpl 的来源
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 的
/**
* 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.
*
*
从注释中的描述,确实是 top-level window decor view。 印证了前面的猜测。
@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);
}
//......
}
继续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
同理,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
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
这里的mDecor 就不像前面的dialog 和popupWindow 中那么容易分析了。
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();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
//......
}
这里代码涉及较多内容,集中注意力,看decorView ,注意到decor = r.window.getDecorView();。
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();
View v = r.activity.mDecor;
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 {
wm.removeViewImmediate(v);
}
}
}
//......
}
至此,通过看了dialog, popupwindow, 和activity 中的部分源码, 知道了普通界面(dialog, popupwindow, 和activity )的第一个view, 是怎么来的,是如何加入到系统中的,也就是windowManager。一句话结论
应用层面的界面都是通过windowmanager 加入到framework 中的,ViewRootImpl 是framework对view 的抽象, 界面管理的根节点。留的尾巴
弄清楚第一个的问题后,接下来争取比较完整地回顾一下view 的创建,绘制,layout过程。to be continued...
文章标题:源码解析:dialog, popupwindow, 和activity 的第一个view是怎么来的?
文章链接:http://soscw.com/essay/26565.html