Android setContentView源码解析
来源:互联网 发布:vscode 设置字体 编辑:程序博客网 时间:2024/05/02 23:52
从入门应用Hello World 说起,我们简单的用setContentView设置了一个布局,然后启动我们的应用,手机界面就显示出Hello World的布局,这个背后到底发生了什么,这就是我今天所研究的对象。
1.概述
首先弄清楚几个跟View有关的概念
窗口(Window):这是一个纯语义的说法,即程序员所看到的屏幕上的某个独立的界面,比如一个带有Title Bar 的Activity 界面、一个对话框、一个Menu 菜单等,这些都称之为窗口。这里所说的窗口管理一般也都泛指所有这些窗口,在Android 的英文相关文章中则直接使用Window 这个单词。而从WmS 的角度来讲,窗口是接收用户消息的最小单元,WmS 内部用特定的类表示一个窗口,以实现对窗口的管理。WmS 接收到用户消息后,首先要判断这个消息属于哪个窗口,然后通过IPC 调用把这个消息传递给客户端的ViewRoot 类。
Window 类:该类在android.view 包中,是一个abstract 类,该类是对包含有可视界面的窗口的一种包装。所谓的可视界面就是指各种View 或者ViewGroup,一般可以通过res/layout 目录下的xml 文件描述。
ViewRoot 类:该类在android.view 包中,客户端申请创建窗口时需要一个客户端代理,用以和WmS 进行交互,这个就是ViewRoot 的功能,每个客户端的窗口都会对应一个ViewRoot 类。
W 类:该类是ViewRoot 类的一个内部类,继承于Binder,用于向WmS 提供一个IPC 接口,从而让WmS 控制窗口客户端的行为。
描述一个窗口之所以使用这么多类的原因在于,窗口的概念存在于客户端和服务端(WmS)之中,客户端所理解的窗口和服务端理解的窗口是不同的,因此,在客户端和服务端会用不同的类来描述窗口。同时,无论是在客户端还是服务端,对窗口都有不同层面的抽象,比如在客户端,用户能看到的窗口一般是View 或者ViewGroup 组成的窗口,而与Activity 对应的窗口却是一个DecorView 类,而具备常规Phone 操作接口的窗口却又是一个PhoneWindow 类。
关系图盗用老罗的图片描述下
至于一个手机窗口的具体组成,网上也有图描述的很清楚了
一般来说DecoView包含一个LinearLayout,(这里的一般指的是R.Layout.screen_title后续会有介绍)这个LinearLayout又包含标题和内容区域两个FramLayout,用SDK中tools文件夹下hierarchyviewer bat 查看ViewTree可以证实这一点
同样可以在代码中证实这一点
获取ContentView: findViewById(android.R.id.content)
获取ContentView上一级:ContentView.getParent
获取DecorView:上面的对象再getParent 或者直接 getWindow().getDecorView()
理论说了这么多,talking is cheap, show me the code!
2.setContentView源码解析
Acvitivity.setContentView
/** * Set the activity content from a layout resource. The resource will be * inflated, adding all top-level views to the activity. * * @param layoutResID Resource ID to be inflated. */public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID);} public Window getWindow() { return mWindow;}
getWindow()到底是个什么鬼,打印一下发现是 com.android.internal.policy.impl.PhoneWindow
接下来看 PhoneWindow.setContentView
@Overridepublic void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); // 1、生成DecorView和mContentParent } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent);// 2、将layoutResId的布局添加到mContentParent中 final Callback cb = getCallback();<span style="font-family: Arial, Helvetica, sans-serif;">// 3、窗口回调函数</span> if (cb != null) { cb.onContentChanged(); }}
我们可以看到,setContentView的基本流程简单概括就是如下几步:
1、构建mDecor对象和mContentParent。mDecor就是整个窗口的顶层视图,它一般包含了Title和ContentView两个区域 ,Title区域就是我们的标题栏,ContentView区域就是显示我们xml布局内容中的区域。
2、设置一些关于窗口的属性,初始化标题栏区域和内容显示区域;
3、调用Window的回调方法。
下面详细解读上面三个部分
2.1 构建mDecor对象和mContentParent
重点分析PhoneWindow.installDecor函数
// 构建mDecor对象,并且初始化标题栏和Content Parent(我们要显示的内容区域) private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); // 1、构建DecorView mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); } if (mContentParent == null) { mContentParent = generateLayout(mDecor); // 2、获取ContentView容器,即显示内容的区域 mTitleView = (TextView)findViewById(com.android.internal.R.id.title); //3、设置Title等 if (mTitleView != null) { if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) { View titleContainer = findViewById(com.android.internal.R.id.title_container); if (titleContainer != null) { titleContainer.setVisibility(View.GONE); } else { mTitleView.setVisibility(View.GONE); } if (mContentParent instanceof FrameLayout) { ((FrameLayout)mContentParent).setForeground(null); } } else { mTitleView.setText(mTitle); } } }} protected DecorView generateDecor() { return new DecorView(getContext(), -1); // 构建mDecor对象}
接着看PhoneWindow.generateLayout,这个函数有点长,也是本文分析的重点,稍微精简一下
// 返回用于显示我们设置的页面内容的ViewGroup容器 protected ViewGroup generateLayout(DecorView decor) { // Apply data from current theme. // 1、获取窗口的Style属性 TypedArray a = getWindowStyle(); if (false) { System.out.println("From style:"); String s = "Attrs:"; for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) { s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "=" + a.getString(i); } System.out.println(s); } // 窗口是否是浮动的 mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false); int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR) & (~getForcedWindowFlags()); if (mIsFloating) { setLayout(WRAP_CONTENT, WRAP_CONTENT); setFlags(0, flagsToUpdate); } else { setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate); } // 设置是否不显示title区域 if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) { requestFeature(FEATURE_NO_TITLE); } // 设置全屏的flag if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) { setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN&(~getForcedWindowFlags())); } if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) { setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags())); } WindowManager.LayoutParams params = getAttributes(); // 设置输入法模式 if (!hasSoftInputMode()) { params.softInputMode = a.getInt( com.android.internal.R.styleable.Window_windowSoftInputMode, params.softInputMode); } if (a.getBoolean(com.android.internal.R.styleable.Window_backgroundDimEnabled, mIsFloating)) { /* All dialogs should have the window dimmed */ if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) { params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND; } params.dimAmount = a.getFloat( android.R.styleable.Window_backgroundDimAmount, 0.5f); } // 窗口动画 if (params.windowAnimations == 0) { params.windowAnimations = a.getResourceId( com.android.internal.R.styleable.Window_windowAnimationStyle, 0); } // The rest are only done if this window is not embedded; otherwise, // the values are inherited from our container. if (getContainer() == null) { if (mBackgroundDrawable == null) { if (mBackgroundResource == 0) { mBackgroundResource = a.getResourceId( com.android.internal.R.styleable.Window_windowBackground, 0); } if (mFrameResource == 0) { mFrameResource = a.getResourceId(com.android.internal.R.styleable.Window_windowFrame, 0); } if (false) { System.out.println("Background: " + Integer.toHexString(mBackgroundResource) + " Frame: " + Integer.toHexString(mFrameResource)); } } mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000); } // Inflate the window decor. // 2、根据一些属性来选择不同的顶层视图布局,例如设置了FEATURE_NO_TITLE的属性,那么就选择没有Title区域的那么布局; // layoutResource布局就是整个Activity的布局,其中含有title区域和content区域,content区域就是用来显示我通过 // setContentView设置进来的内容区域,也就是我们要显示的视图。 int layoutResource; int features = getLocalFeatures(); // System.out.println("Features: 0x" + Integer.toHexString(features)); if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { if (mIsFloating) { layoutResource = com.android.internal.R.layout.dialog_title_icons; } else { layoutResource = com.android.internal.R.layout.screen_title_icons; } // System.out.println("Title Icons!"); } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0) { // Special case for a window with only a progress bar (and title). // XXX Need to have a no-title version of embedded windows. layoutResource = com.android.internal.R.layout.screen_progress; // System.out.println("Progress!"); } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) { // Special case for a window with a custom title. // If the window is floating, we need a dialog layout if (mIsFloating) { layoutResource = com.android.internal.R.layout.dialog_custom_title; } else { layoutResource = com.android.internal.R.layout.screen_custom_title; } } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) { // If no other features and not embedded, only need a title. // If the window is floating, we need a dialog layout if (mIsFloating) { layoutResource = com.android.internal.R.layout.dialog_title; } else { layoutResource = com.android.internal.R.layout.screen_title; } // System.out.println("Title!"); } else { // Embedded, so no decoration is needed. layoutResource = com.android.internal.R.layout.screen_simple; // System.out.println("Simple!"); } mDecor.startChanging(); // 3、加载视图 View in = mLayoutInflater.inflate(layoutResource, null); // 4、将layoutResource的内容添加到mDecor中 decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); // 5、获取到我们的内容显示区域,这是一个ViewGroup类型的,其实是FrameLayout ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); if (contentParent == null) { throw new RuntimeException("Window couldn't find content container view"); } if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { ProgressBar progress = getCircularProgressBar(false); if (progress != null) { progress.setIndeterminate(true); } } // 6、设置一些背景、title等属性 // Remaining setup -- of background and title -- that only applies // to top-level windows. if (getContainer() == null) { Drawable drawable = mBackgroundDrawable; if (mBackgroundResource != 0) { drawable = getContext().getResources().getDrawable(mBackgroundResource); } mDecor.setWindowBackground(drawable); drawable = null; if (mFrameResource != 0) { drawable = getContext().getResources().getDrawable(mFrameResource); } mDecor.setWindowFrame(drawable); } return contentParent; }
第一直觉就是看返回的是个神马东东,
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);这不就是id为content的View吗?
该方法会做如下事情:
1、根据窗口的风格修饰类型为该窗口选择不同的窗口布局文件(根视图)。这些窗口修饰布局文件指定一个用来存放
Activity自定义布局文件的ViewGroup视图,一般为FrameLayout 其id 为: android:id="@android:id/content"。
例如窗口修饰类型包括FullScreen(全屏)、NoTitleBar(不含标题栏)等。选定窗口修饰类型有两种:
①、指定requestFeature()指定窗口修饰符,PhoneWindow对象调用getLocalFeature()方法获取值;
②、为我们的Activity配置相应属性,即android:theme=“”,PhoneWindow对象调用getWindowStyle()方法
获取值。
举例如下,隐藏标题栏有如下方法:requestWindowFeature(Window.FEATURE_NO_TITLE);
或者 为Activity配置xml属性:android:theme=”@android:style/Theme.NoTitleBar”。
看到这里很好理解:在Activity中必须在setContentView之前调用requestFeature()方法。
2. 确定好窗口风格之后,选定该风格对应的布局文件,这些布局文件位于 frameworks/base/core/res/layout/ ,在sdk下面也可以看到。
典型的窗口布局文件有:
R.layout.dialog_titile_icons
R.layout.screen_title_icons
R.layout.screen_progress
R.layout.dialog_custom_title
R.layout.dialog_title
R.layout.screen_title // 最常用的Activity窗口修饰布局文件
R.layout.screen_simple //全屏的Activity窗口布局文件
R.layout.screen_title就是HelloWorld应用用到的布局,我们一起看下具体布局
如果没有源码的话,sdk下面也可以看到(sdk\platforms\android-5.0.1\data\res\layout)
<!--This is an optimized layout for a screen, with the minimum set of featuresenabled.--><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:fitsSystemWindows="true"> <!-- Popout bar for action modes --> <ViewStub android:id="@+id/action_mode_bar_stub" android:inflatedId="@+id/action_mode_bar" android:layout="@layout/action_mode_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="?attr/actionBarTheme" /> <FrameLayout android:layout_width="match_parent" android:layout_height="?android:attr/windowTitleSize" style="?android:attr/windowTitleBackgroundStyle"> <TextView android:id="@android:id/title" style="?android:attr/windowTitleStyle" android:background="@null" android:fadingEdge="horizontal" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout> <FrameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" android:foregroundGravity="fill_horizontal|top" android:foreground="?android:attr/windowContentOverlay" /></LinearLayout>
该布局文件很简单,一个LinearLayout下包含了两个子FrameLayout视图,
第一个FrameLayout用来显示标题栏(TitleBar),该TextView 视图id为title(android:id="@android:id/title");
第二个FrameLayout用来显示我们Activity的布局文件的父视图, 该FrameLayoutid为content(android:id="@android:id/content") 。
这一点与用hierarchyviewer工具前面看到的布局结构一致。
2.2 加载资源文件到contentView
我们确定窗口修饰布局文件后,mDecor做为根视图将该窗口布局对应的视图添加进去,并且获取id为content
的View,将其赋值给mContentParent对象,即我们前面中提到的第二个FrameLayout。这样Decor
View里面有内容了。再次回到PhoneWindow.setContentView()
@Override public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent); final Callback cb = getCallback(); if (cb != null) { cb.onContentChanged(); } }这里已经获取到mConetentParent对象是id为content的FramLayout对象了,接下来把我们应用中设置的layoutResID解析到到此FramLayout中,这里可以知道为什么android叫这个函数为setContentView而不是setView了,因为最顶层的View也就是DecorView的布局文件都是系统预置好的,根据不同的getLocalFeature()来选择加载系统中预置的某一个布局文件,DecorView确定以后,setContentView就是给id为content的View来设置内容了。
// 为节省篇幅,删除一些调试代码 public View inflate(int resource, ViewGroup root) { return inflate(resource, root, root != null); } public View inflate(int resource, ViewGroup root, boolean attachToRoot) { XmlResourceParser parser = getContext().getResources().getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); } } public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { final AttributeSet attrs = Xml.asAttributeSet(parser); Context lastContext = (Context)mConstructorArgs[0]; mConstructorArgs[0] = mContext; View result = root; try { // Look for the root node. int type; while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { // Empty } if (type != XmlPullParser.START_TAG) { throw new InflateException(parser.getPositionDescription() + ": No start tag found!"); } final String name = parser.getName(); // 根标签为merge时,root不能为空 if (TAG_MERGE.equals(name)) { if (root == null || !attachToRoot) { throw new InflateException("<merge /> can be used only with a valid " + "ViewGroup root and attachToRoot=true"); } rInflate(parser, root, attrs, false); } else { // Temp is the root view that was found in the xml View temp; if (TAG_1995.equals(name)) { temp = new BlinkLayout(mContext, attrs); } else { temp = createViewFromTag(root, name, attrs); } ViewGroup.LayoutParams params = null; if (root != null) { // Create layout params that match root, if supplied params = root.generateLayoutParams(attrs); if (!attachToRoot) { // Set the layout params for temp if we are not attaching. // (If we are, we use addView, below) temp.setLayoutParams(params); } } // 解析temp的所有子View rInflate(parser, temp, attrs, true); // 将temp添加到root中 if (root != null && attachToRoot) { root.addView(temp, params); } // 如果未指定root或者不附加到root,则返回xml所代表的view; if (root == null || !attachToRoot) { result = temp; } } } catch (XmlPullParserException e) { InflateException ex = new InflateException(e.getMessage()); ex.initCause(e); throw ex; } catch (IOException e) { InflateException ex = new InflateException(parser.getPositionDescription()+ ": " + e.getMessage()); ex.initCause(e); throw ex; } finally { // Don't retain static reference on context. mConstructorArgs[0] = lastContext; mConstructorArgs[1] = null; } return result; } }
这里又会调用到rInflate方法
void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { final int depth = parser.getDepth(); int type; while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { if (type != XmlPullParser.START_TAG) { continue; } final String name = parser.getName(); if (TAG_REQUEST_FOCUS.equals(name)) { parseRequestFocus(parser, parent); } else if (TAG_INCLUDE.equals(name)) { if (parser.getDepth() == 0) { throw new InflateException("<include /> cannot be the root element"); } parseInclude(parser, parent, attrs); } else if (TAG_MERGE.equals(name)) { throw new InflateException("<merge /> must be the root element"); } else if (TAG_1995.equals(name)) { final View view = new BlinkLayout(mContext, attrs); final ViewGroup viewGroup = (ViewGroup) parent; final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); rInflate(parser, view, attrs, true); viewGroup.addView(view, params); } else { final View view = createViewFromTag(parent, name, attrs); final ViewGroup viewGroup = (ViewGroup) parent; final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); rInflate(parser, view, attrs, true); viewGroup.addView(view, params); } } if (finishInflate) parent.onFinishInflate(); }首先处理了几个标签
看了这里,同学们就会明白,为什么不建议在布局文件中做过多地View嵌套了吧,层层递归啊
2.3.通知Callback,回调onContentChanged方法
这里的callback是个什么东东,老办法打印一下发现就是我们自己的MainActivity对象,很显然我们自己的Activity是没重写onContentChanged这个方法的,向上查找发现Activity.java的代码final void attach(...) { ... mWindow = PolicyManager.makeNewWindow(this); mWindow.setCallback(this); ... } public void onContentChanged() { }
这个callback设置的就是Activity自己本身,是在attach()方法被调用的时候生成的,而默认的onContentChanged是空的,我们自己的MainActivity可以重写这个方法来监听ContentView发生改变的事件。
小结一下整个过程:
1、创建一个DecorView对象,该对象将作为整个应用窗口的根视图
2、根据theam值或代码requestFeature()值,调用getLocalFeature()获取feature值,进而DecorView加载不同的布局文件
3、获取Activity的布局文件存放的位置(mContentParent)即DecorView布局中id为content的FrameLayout指定 。
4、将Activity的布局文件添加至id为content的FrameLayout(mContentParent)内。
最后,当AMS(ActivityManagerService)准备resume一个Activity时,会回调该Activity的handleResumeActivity()方法,
该方法会调用Activity的makeVisible方法 ,显示我们刚才创建的mDecor 视图。
3.怎样加载到Activity
之前研究过Activity启动原理,会调用到ActivityThread#handlerLaunchActivity方法
public final class ActivityThread {......private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {......Activity a = performLaunchActivity(r, customIntent);if (a != null) {r.createdConfig = new Configuration(mConfiguration);Bundle oldState = r.state;handleResumeActivity(r.token, false, r.isForward);......} else {......}}......}
接着看ActivityThread#performLaunchActivity
public final class ActivityThread {......private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {ActivityInfo aInfo = r.activityInfo;if (r.packageInfo == null) {r.packageInfo = getPackageInfo(aInfo.applicationInfo,Context.CONTEXT_INCLUDE_CODE);}ComponentName component = r.intent.getComponent();if (component == null) {component = r.intent.resolveActivity(mInitialApplication.getPackageManager());r.intent.setComponent(component);}if (r.activityInfo.targetActivity != null) {component = new ComponentName(r.activityInfo.packageName,r.activityInfo.targetActivity);}Activity activity = null;try {java.lang.ClassLoader cl = r.packageInfo.getClassLoader();activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);r.intent.setExtrasClassLoader(cl);if (r.state != null) {r.state.setClassLoader(cl);}} catch (Exception e) {......}try {Application app = r.packageInfo.makeApplication(false, mInstrumentation);......if (activity != null) {ContextImpl appContext = new ContextImpl();appContext.init(r.packageInfo, r.token, this);appContext.setOuterContext(activity);CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());Configuration config = new Configuration(mConfiguration);......activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstance,r.lastNonConfigurationChildInstances, config);if (customIntent != null) {activity.mIntent = customIntent;}r.lastNonConfigurationInstance = null;r.lastNonConfigurationChildInstances = null;activity.mStartedActivity = false;int theme = r.activityInfo.getThemeResource();if (theme != 0) {activity.setTheme(theme);}activity.mCalled = false;mInstrumentation.callActivityOnCreate(activity, r.state);......r.activity = activity;r.stopped = true;if (!r.activity.mFinished) {activity.performStart();r.stopped = false;}if (!r.activity.mFinished) {if (r.state != null) {mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);}}if (!r.activity.mFinished) {activity.mCalled = false;mInstrumentation.callActivityOnPostCreate(activity, r.state);if (!activity.mCalled) {throw new SuperNotCalledException("Activity " + r.intent.getComponent().toShortString() +" did not call through to super.onPostCreate()");}}}r.paused = true;mActivities.put(r.token, r);} catch (SuperNotCalledException e) {......} catch (Exception e) {......}return activity;}......}主要做以下工作
1.收集要启动的Activity的相关信息,主要package和component信息
2.通过ClassLoader将要启动的Activity类加载进来
3.根据AndroidManifest.xml配置文件中的Application标签的信息来创建Application
4.通过attach方法将这些上下文信息设置到MainActivity中
5.调用Activity的onCreate方法
上面的第5步就会调用到本文中研究到的的setContentView方法,DecorView已经创建并初始化完毕,并且Activity的布局文件已经成功添加到DecorView的mContentParent中,但是DecorView并没有添加到WindowManager中去,也无法接收外界的输入,只有到Acitivity的makeVisible()被调用时,DecorView才真正完成了添加和显示过程。再回到handleLaunchActivity中,调完performLaunchActivity之后,接着调用handleResumeActivity,我们看ActivityThread#handleResumeActivity
//系统resume一个Activity时,调用此方法 final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) { ActivityRecord r = performResumeActivity(token, clearHide); //... if (r.activity.mVisibleFromClient) { r.activity.makeVisible(); } }
接下来看Activity#makeVisible方法
void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }handleLaunchActivity调用完DecoView已经显示完全,后面怎么把已经创建好的窗口通知给WindowManagerService 以便它能够把这个窗口显示在屏幕上,后续研究请移步到View的绘制流程。
- Android setContentView源码解析
- Android setContentView源码解析
- Android源码解析setContentView
- Android setContentView()源码解析
- Android setContentView()源码解析
- android setContentView源码解析
- Android 源码解析 之 setContentView
- Android 源码解析 之 setContentView
- Android 源码解析 之 setContentView
- Android setContentView 加载布局源码解析
- Android源码解析Activity#setContentView()方法
- Android源码解析Activity#setContentView()方法
- Android源码解析Activity#setContentView()方法
- setContentView源码解析
- setContentView源码解析
- 系统入门(5):Android 源码解析 之 setContentView
- Android应用setContentView与LayoutInflater加载解析机制源码分析
- Android应用setContentView与LayoutInflater加载解析机制源码分析
- Git与Repo入门
- 初识压缩感知
- 手机浏览器判断app是否安装并启动
- 软件开发中的结构图
- online_judge_1145
- Android setContentView源码解析
- python pydot安装
- 1104. Sum of Number Segments
- jfinal2
- NSURLSession实现Http传输的简单实现
- 初识卷积神经网
- 第二章 Spring MVC入门
- Zygote的启动过程
- Android的学习路线(Important)