Android setContentView源码解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tianmi1988/article/details/50389560

从入门应用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

@Override
public 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 features
enabled.
-->

<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来设置内容了。
具体的加载流程移步LayoutInflater.inflate
// 为节省篇幅,删除一些调试代码 
    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();
    }
首先处理了几个标签
TAG_MERGE = "merge";    TAG_INCLUDE = "include";    TAG_1995 = "blink";    TAG_REQUEST_FOCUS = "requestFocus";
然后在else分支,会  递归  调用rInflate(...),将xml的子控件添加到parent中,生成完整的  contentView
看了这里,同学们就会明白,为什么 不建议在布局文件中做过多地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的FrameLayoutmContentParent)内。

       最后,当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的绘制流程







猜你喜欢

转载自blog.csdn.net/tianmi1988/article/details/50389560