从setContentView()谈起

从setContentView()谈起

本文主要讲解View或者ViewGroup是如何添加到应用程序的窗口中的。

1. 最简单的Activity

一个Activity最简单的结构是如下:

     public class MainActivity extends Activity {
        private Button taskSwitch;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //那就从这个setContentView开启我们的View或者ViewGroup加载之旅吧
            setContentView(R.layout.activity_main);
        }
    }

R.layout.activity_main.xml结构如下:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context=".MainActivity" >
    
        <Button android:id="@+id/taskSwitch"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/taskSwitch"/>
    
    </RelativeLayout>

2. setContentView关键流程顺序图

Step1 Activity.setContentView()

这个方法是Activity里面的方法,直接回调了Window的getContentView(),而mWindow对象实际上是PhoneWindow对象。
frameworks/base/core/java/android/app/Activity.java

    public class Activity extends ContextThemeWrapper                                                                       
            implements LayoutInflater.Factory2,                                                                             
            Window.Callback, KeyEvent.Callback,                                                                             
            OnCreateContextMenuListener, ComponentCallbacks2 {
            
        public void setContentView(int layoutResID) {  
            getWindow().setContentView(layoutResID);  
        }  
          
        public Window getWindow() {  
            return mWindow;   //Window对象,实际上是一个PhoneWindow对象,它是在AMS初始化的时候创建的  
        }
    }

AMS初始化的时候会调用PolicyManager.makeNewWindow(this);

    public class Activity extends ContextThemeWrapper                                                                       
            implements LayoutInflater.Factory2,                                                                             
            Window.Callback, KeyEvent.Callback,                                                                             
            OnCreateContextMenuListener, ComponentCallbacks2 {
            
        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) {
            attachBaseContext(context);
            
            mFragments.attachActivity(this, mContainer, null);
             
            mWindow = PolicyManager.makeNewWindow(this); //这里创建的实际上是PhoneWindow对象
            mWindow.setCallback(this);
            mWindow.getLayoutInflater().setPrivateFactory(this);                                                            
            ……
        } 
    }

而PolicyManager是调用的IPolicy的接口实现穿件Window的。

    public final class PolicyManager {
         private static final String POLICY_IMPL_CLASS_NAME =
             "com.android.internal.policy.impl.Policy";
             
         private static final IPolicy sPolicy;
         ……
         public static Window makeNewWindow(Context context) {                                                                
             return sPolicy.makeNewWindow(context);                                                                           
         }
         ……
    }

frameworks/base/policy/src/com/android/internal/policy/impl/Policy.java实现了IPolicy接口,创建了Window对象,实际上是PhoneWindow对象

     // Simple implementation of the policy interface that spawns the right
     // set of objects
     public class Policy implements IPolicy {                                                                                 
         private static final String TAG = "PhonePolicy"; 
         ……
         public Window makeNewWindow(Context context) {
         return new PhoneWindow(context); //创建了Window对象,实际上是PhoneWindow对象
         }
    }

到此,我们知道Activity.setContentView()最终调用到的是PhoneWindow.setContentView()。

Step2 PhoneWindow.setContentView()

如果第一次调用mDecor和mContentParent对象都为null,会调用installDecor(),如果不是,则调用mContentParent.removeAllViews()来删除所有的子View,然后inflate所以的View,再添加到mContentParent中。

    public class PhoneWindow extends Window implements MenuBuilder.Callback {
         @Override
         public void setContentView(int layoutResID) {
             //如果第一次调用mDecor和mContentParent对象都为null,会调用installDecor(),如果不是,则调用mContentParent.removeAllViews()
             if (mContentParent == null) {
                 installDecor();
             } else {
                 mContentParent.removeAllViews();
             }
             mLayoutInflater.inflate(layoutResID, mContentParent);
             final Callback cb = getCallback();
             if (cb != null && !isDestroyed()) {
                 cb.onContentChanged();
             }
         }
    }

Step2.1 PhoneWindow. installDecor()

    public class PhoneWindow extends Window implements MenuBuilder.Callback {
    
        private void installDecor() {
            if (mDecor == null) {
                mDecor = generateDecor();
                mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
                mDecor.setIsRootNamespace(true);
                if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                    mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
                }
            }
            if (mContentParent == null) {
                mContentParent = generateLayout(mDecor);
    
                // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
                mDecor.makeOptionalFitsSystemWindows();
    
                mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
                if (mTitleView != null) {
                    mTitleView.setLayoutDirection(mDecor.getLayoutDirection());
                    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);
                    }
                } else {
                    mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
                    if (mActionBar != null) {
                        mActionBar.setWindowCallback(getCallback());
                        if (mActionBar.getTitle() == null) {
                            mActionBar.setWindowTitle(mTitle);
                        }
                        final int localFeatures = getLocalFeatures();
                        if ((localFeatures & (1 << FEATURE_PROGRESS)) != 0) {
                            mActionBar.initProgress();
                        }
                        if ((localFeatures & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
                            mActionBar.initIndeterminateProgress();
                        }
    
                        final ActionBarOverlayLayout abol = (ActionBarOverlayLayout) findViewById(
                                com.android.internal.R.id.action_bar_overlay_layout);
                        if (abol != null) {
                            abol.setOverlayMode(
                                    (localFeatures & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0);
                        }
    
                        boolean splitActionBar = false;
                        final boolean splitWhenNarrow =
                                (mUiOptions & ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW) != 0;
                        if (splitWhenNarrow) {
                            splitActionBar = getContext().getResources().getBoolean(
                                    com.android.internal.R.bool.split_action_bar_is_narrow);
                        } else {
                            splitActionBar = getWindowStyle().getBoolean(
                                    com.android.internal.R.styleable.Window_windowSplitActionBar, false);
                        }
                        final ActionBarContainer splitView = (ActionBarContainer) findViewById(
                                com.android.internal.R.id.split_action_bar);
                        if (splitView != null) {
                            mActionBar.setSplitView(splitView);
                            mActionBar.setSplitActionBar(splitActionBar);
                            mActionBar.setSplitWhenNarrow(splitWhenNarrow);
    
                            final ActionBarContextView cab = (ActionBarContextView) findViewById(
                                    com.android.internal.R.id.action_context_bar);
                            cab.setSplitView(splitView);
                            cab.setSplitActionBar(splitActionBar);
                            cab.setSplitWhenNarrow(splitWhenNarrow);
                        } else if (splitActionBar) {
                            Log.e(TAG, "Requested split action bar with " +
                                    "incompatible window decor! Ignoring request.");
                        }
    
                        if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||
                                (mIconRes != 0 && !mActionBar.hasIcon())) {
                            mActionBar.setIcon(mIconRes);
                        } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&
                                mIconRes == 0 && !mActionBar.hasIcon()) {
                            mActionBar.setIcon(
                                    getContext().getPackageManager().getDefaultActivityIcon());
                            mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;
                        }
                        if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||
                                (mLogoRes != 0 && !mActionBar.hasLogo())) {
                            mActionBar.setLogo(mLogoRes);
                        }
    
                        // Post the panel invalidate for later; avoid application onCreateOptionsMenu
                        // being called in the middle of onCreate or similar.
                        mDecor.post(new Runnable() {
                            public void run() {
                                // Invalidate if the panel menu hasn't been created before this.
                                PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
                                if (!isDestroyed() && (st == null || st.menu == null)) {
                                    invalidatePanelMenu(FEATURE_ACTION_BAR);
                                }
                            }
                        });
                    }
                }
            }
        }
    }

首先判断mDecor是否为空,如果是空,则调用generateDecor()创建一个新的DecorView赋值给mDecor,该类是一个FrameLayout的子类,是一个ViewGroup,当用HierarchyViewer查看Layoutt的时候每个窗口最顶层都有的一个FrameLayout,这个FrameLayout就是这边的这个DecorView。

    protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
    }

    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
    }

然后判断mContentParent是否为空,如果为空,则调用generateLayout(mDecor)创建mContentParent。

    protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.
        
        // 1. 从com.android.internal.R.styleable.Window也就是android:theme=“”配置的值中获取Window的Style属性
        // 2. 通过requestFeature方法设置Window.mFeatures和PhoneWindow.mLocalFeatures属性,设置宽口风格
        
        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);
        }

        if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);
        } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestFeature(FEATURE_ACTION_BAR);
        }

        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {
            requestFeature(FEATURE_ACTION_BAR_OVERLAY);
        }

        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionModeOverlay, false)) {
            requestFeature(FEATURE_ACTION_MODE_OVERLAY);
        }

        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_windowTranslucentStatus,
                false)) {
            setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS
                    & (~getForcedWindowFlags()));
        }

        if (a.getBoolean(com.android.internal.R.styleable.Window_windowTranslucentNavigation,
                false)) {
            setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION
                    & (~getForcedWindowFlags()));
        }

        if (a.getBoolean(com.android.internal.R.styleable.Window_windowOverscan, false)) {
            setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));
        }

        if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {
            setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));
        }

        if (a.getBoolean(com.android.internal.R.styleable.Window_windowEnableSplitTouch,
                getContext().getApplicationInfo().targetSdkVersion
                        >= android.os.Build.VERSION_CODES.HONEYCOMB)) {
            setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));
        }

        a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
        a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor)) {
            if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
            a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor,
                    mFixedWidthMajor);
        }
        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor)) {
            if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
            a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor,
                    mFixedWidthMinor);
        }
        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor)) {
            if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
            a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor,
                    mFixedHeightMajor);
        }
        if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor)) {
            if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
            a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor,
                    mFixedHeightMinor);
        }

        // 3.读取SDK版本信息
        final Context context = getContext();
        final int targetSdk = context.getApplicationInfo().targetSdkVersion;
        final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
        final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
        final boolean targetHcNeedsOptions = context.getResources().getBoolean(
                com.android.internal.R.bool.target_honeycomb_needs_options_menu);
        final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);

        if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
            addFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
        } else {
            clearFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
        }
        
        if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion
                >= android.os.Build.VERSION_CODES.HONEYCOMB) {
            if (a.getBoolean(
                    com.android.internal.R.styleable.Window_windowCloseOnTouchOutside,
                    false)) {
                setCloseOnTouchOutsideIfNotSet(true);
            }
        }
        
        // 4. 获取LayoutParams信息用来判断是否输入法、是否模糊后面的,是否有窗口动画
        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;
            }
            if (!haveDimAmount()) {
                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.

        // 5. 获取上面设置的PhoneWindow.mLocalFeatures,根据一些列的条件判断需要是哪个layoutResource,比如是否是全屏,是否有标题栏
        // 有如下7种类型的layout:
        // com.android.internal.R.layout.screen_title_icons
        // com.android.internal.R.layout.screen_progress
        // com.android.internal.R.layout.screen_custom_title
        // com.android.internal.R.layout.screen_action_bar
        // com.android.internal.R.layout.screen_title
        // com.android.internal.R.layout.screen_simple_overlay_action_mode
        // com.android.internal.R.layout.screen_simple
        
        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) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = com.android.internal.R.layout.screen_title_icons;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
            // System.out.println("Title Icons!");
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                && (features & (1 << FEATURE_ACTION_BAR)) == 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) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = com.android.internal.R.layout.screen_custom_title;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
        } 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) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                layoutResource = com.android.internal.R.layout.screen_action_bar;
            } else {
                layoutResource = com.android.internal.R.layout.screen_title;
            }
            // System.out.println("Title!");
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = com.android.internal.R.layout.screen_simple;
            // System.out.println("Simple!");
        }

        mDecor.startChanging();

        // 6. 填充相应的layoutResource,并把它加到decor里面
        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

        // 7. 找出相应的ID_ANDROID_CONTENT的ViewGroup,实际想就是上面layoutResource里面的<FrameLayout android:id="@android:id/content" /> 这个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);
            }
        }

        // 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);

            // System.out.println("Text=" + Integer.toHexString(mTextColor) +
            // " Sel=" + Integer.toHexString(mTextSelectedColor) +
            // " Title=" + Integer.toHexString(mTitleColor));

            if (mTitleColor == 0) {
                mTitleColor = mTextColor;
            }

            if (mTitle != null) {
                setTitle(mTitle);
            }
            setTitleColor(mTitleColor);
        }

        mDecor.finishChanging();

        return contentParent;
    }

此时,我们拿一个常用的com.android.internal.R.layout.screen_title窗口布局文件来分析一下。
这个窗口布局文件分为3个部分:

  1. action mode
  2. 标题栏内容区域
  3. Activity的SetContentView的Layout文件实际放置的俯视图就是这个id为conten的FrameLayout
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:fitsSystemWindows="true"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <!-- 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" />
        <RelativeLayout android:id="@android:id/title_container"
            style="?android:attr/windowTitleBackgroundStyle"
            android:layout_width="match_parent"
            android:layout_height="?android:attr/windowTitleSize">
            <!-- The title background has 9px left padding. -->
            <ImageView android:id="@android:id/left_icon"
                android:visibility="gone"
                android:layout_marginEnd="9dip"
                android:layout_width="16dip"
                android:layout_height="16dip"
                android:scaleType="fitCenter"
                android:layout_alignParentStart="true"
                android:layout_centerVertical="true" />
            <ProgressBar android:id="@+id/progress_circular"
                style="?android:attr/progressBarStyleSmallTitle"
                android:visibility="gone"
                android:max="10000"
                android:layout_centerVertical="true"
                android:layout_alignParentEnd="true"
                android:layout_marginStart="6dip"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
            <!-- There are 6dip between this and the circular progress on the right, we
                 also make 6dip (with the -3dip margin_left) to the icon on the left or
                 the screen left edge if no icon. This also places our left edge 3dip to
                 the left of the title text left edge. -->
            <ProgressBar android:id="@+id/progress_horizontal"
                style="?android:attr/progressBarStyleHorizontal"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="-3dip"
                android:layout_toStartOf="@android:id/progress_circular"
                android:layout_toEndOf="@android:id/left_icon"
                android:layout_centerVertical="true"
                android:visibility="gone"
                android:max="10000" />
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="horizontal"
                android:layout_toStartOf="@id/progress_circular"
                android:layout_toEndOf="@android:id/left_icon"
                >
                <TextView android:id="@android:id/title"
                    style="?android:attr/windowTitleStyle"
                    android:layout_width="0dip"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:background="@null"
                    android:fadingEdge="horizontal"
                    android:scrollHorizontally="true"
                    android:gravity="center_vertical"
                    android:layout_marginEnd="2dip"
                    />
                <!-- 2dip between the icon and the title text, if icon is present. -->
                <ImageView android:id="@android:id/right_icon"
                    android:visibility="gone"
                    android:layout_width="16dip"
                    android:layout_height="16dip"
                    android:layout_weight="0"
                    android:layout_gravity="center_vertical"
                    android:scaleType="fitCenter"
                    />
                </LinearLayout>
        </RelativeLayout>
        <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>

Step2.2 LayoutInflater.inflate()

前一步结束之后,我们需要的mDecor和mContentParent都已经初始化完毕,mContentParent( )就是我们Activity中setContentView的视图所要填充到的父视图。

    public View inflate(int resource, ViewGroup root) {
        return inflate(resource, root, root != null);
    }

    public View inflate(XmlPullParser parser, ViewGroup root) {
        return inflate(parser, root, root != null);
    }

    public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
        if (DEBUG) System.out.println("INFLATING from resource: " + resource);
        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) {
            trace.tracebegin(trace.trace_tag_view, "inflate");

            final attributeset attrs = xml.asattributeset(parser);
            context lastcontext = (context)mconstructorargs[0];
            mconstructorargs[0] = mcontext;
            view result = root;

            try {
                // 1. 查找并初始化跟试图节点
                // look for the root node.
                
                ……
                final string name = parser.getname();
                

                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) {
                        if (debug) {
                            system.out.println("creating params from root: " +
                                    root);
                        }
                        // 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);
                        }
                    }

                    if (debug) {
                        system.out.println("-----> start inflating children");
                    }
                    // 创建所有的子试图节点
                    // inflate all children under temp
                    rinflate(parser, temp, attrs, true);
                    
                    ……
                }

            } 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;
            }

            trace.traceend(trace.trace_tag_view);

            return result;
        }
    }

实际上mLayoutInflater实际上是PhoneLayoutInflater的对象,可以分析Step1中PhoneWindow的初始化过程得知。可以看如下代码:

    public class PhoneWindow extends Window implements MenuBuilder.Callback {
        public PhoneWindow(Context context) {
            super(context);
            mLayoutInflater = LayoutInflater.from(context);
        }
    }
    
    public abstract class LayoutInflater {
        public static LayoutInflater from(Context context) {
            LayoutInflater LayoutInflater =
                    (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            if (LayoutInflater == null) {
                throw new AssertionError("LayoutInflater not found.");
            }
            return LayoutInflater;
        }
    }
    
    class ContextImpl extends Context {
        static {
            registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
                    public Object createService(ContextImpl ctx) {
                        return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
                    }});
        }
        
        private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP =
        new HashMap<String, ServiceFetcher>();

        private static int sNextPerContextServiceCacheIndex = 0;
        private static void registerService(String serviceName, ServiceFetcher fetcher) {
            if (!(fetcher instanceof StaticServiceFetcher)) {
                fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
            }
            SYSTEM_SERVICE_MAP.put(serviceName, fetcher);
        }

        public Object getSystemService(String name) {
            ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
            return fetcher == null ? null : fetcher.getService(this);
        }
    }
    
    public final class PolicyManager {
        private static final String POLICY_IMPL_CLASS_NAME =
            "com.android.internal.policy.impl.Policy";
    
        private static final IPolicy sPolicy;
    
        static {
            // Pull in the actual implementation of the policy at run-time
            try {
                Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
                sPolicy = (IPolicy)policyClass.newInstance();
            } 
            ……
        }
        public static LayoutInflater makeNewLayoutInflater(Context context) {
            return sPolicy.makeNewLayoutInflater(context);
        }
    }


    public class Policy implements IPolicy {
        public LayoutInflater makeNewLayoutInflater(Context context) {
            return new PhoneLayoutInflater(context);
        }
    }

至此,总的流程都已经结束了,我们的Acitivity.setContentView就差不多分析完毕了。总的来说上面的过程可以概括为3步:

1、创建一个DecorView对象,该对象将作为整个应用窗口的根视图
2、创建不同的窗口布局文件,并且获取Activity的布局文件该存放的地方,即该窗口布局文件内id为content的FrameLayout指定() 。
3、将Activity的布局文件添加至id为content的FrameLayout内。

3. 最后我们来分析一下每一个View或者ViewGroup是如何被创建的。

从上面我们可以得知,每一个View或者ViewGroup都是通过调用LayoutInflater.createviewfromtag()来进行View的穿件

    View createViewFromTag(View parent, String name, AttributeSet attrs) {
        if (name.equals("view")) {
            name = attrs.getAttributeValue(null, "class");
        }

        if (DEBUG) System.out.println("******** Creating view: " + name);

        try {
            View view;
            // 此处几个Factory都不会执行
            if (mFactory2 != null) view = mFactory2.onCreateView(parent, name, mContext, attrs);
            else if (mFactory != null) view = mFactory.onCreateView(name, mContext, attrs);
            else view = null;

            if (view == null && mPrivateFactory != null) {
                view = mPrivateFactory.onCreateView(parent, name, mContext, attrs);
            }
            if (view == null) {
            // 根据View是否是内置判断需要调用的方法:
            // 如果是内置的(android.widget或android.webkit或andriod.view包里的)android自身类就调用LayoutInflater.onCreateView (3parameters),如果不是就调用LayoutInflater.createView
            如果不是内置(自定义的,或者目录不在上述几个包中的)则调用LayoutInflater.createView(name, null, attrs);
                if (-1 == name.indexOf('.')) {
                    view = onCreateView(parent, name, attrs);
                } else {
                    view = createView(name, null, attrs);
                }
            }

            if (DEBUG) System.out.println("Created view is: " + view);
            return view;

        ……
    }

根据View是否是内置判断需要调用的方法:
如果是内置的(android.widget或android.webkit或andriod.view包里的)android自身类就调用LayoutInflater.onCreateView (3parameters),如果不是就调用LayoutInflater.createView;
如果不是内置(自定义的,或者目录不在上述几个包中的)则调用LayoutInflater.createView(name, null, attrs);
如果是内置的(android.widget或android.webkit)android自身类就调用LayoutInflater.createView,如果不是就调用LayoutInflater.onCreateView(2 parameters);

    public abstract class LayoutInflater {
        protected View onCreateView(String name, AttributeSet attrs)
                throws ClassNotFoundException {
            return createView(name, "android.view.", attrs);
        }
    
        protected View onCreateView(View parent, String name, AttributeSet attrs)
                throws ClassNotFoundException {
            // 1. 根据多态性,直接调用PhoneLayoutInflater.onCreateView(2 parameters)
            return onCreateView(name, attrs);
        }
    }
    
    public class PhoneLayoutInflater extends LayoutInflater {
        private static final String[] sClassPrefixList = {
            "android.widget.",
            "android.webkit."
        };

        @Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
            // 2. 如果是内置的(android.widget或android.webkit)android自身类就调用LayoutInflater.createView,如果不是就调用LayoutInflater.onCreateView(2 parameters)
            for (String prefix : sClassPrefixList) {
                try {
                    View view = createView(name, prefix, attrs);
                    if (view != null) {
                        return view;
                    }
                } catch (ClassNotFoundException e) {
                    // In this case we want to let the base class take a crack
                    // at it.
                }
            }
    
            // 2. 
            return super.onCreateView(name, attrs);
        }
    }
    
    不过最终都是要调用createView()方法来进行View或者ViewGroup的创建。createView()的方法气势很简答,就是通过反射机制把View或者ViewGroup给创建出来。
    
    public final View createView(String name, String prefix, AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        // 1. 如果sConstructorMap里面已经有对应的类,则直接取出来,
        // 如果没有,则通过发射机制把对应的类给loadClass进来;
        Constructor<? extends View> constructor = sConstructorMap.get(name);
        Class<? extends View> clazz = null;

        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);

            if (constructor == null) {
                // Class not found in the cache, see if it's real, and try to add it
                clazz = mContext.getClassLoader().loadClass(
                        prefix != null ? (prefix + name) : name).asSubclass(View.class);
                
                if (mFilter != null && clazz != null) {
                    boolean allowed = mFilter.onLoadClass(clazz);
                    if (!allowed) {
                        failNotAllowed(name, prefix, attrs);
                    }
                }
                constructor = clazz.getConstructor(mConstructorSignature);
                sConstructorMap.put(name, constructor);
            } else {
                // If we have a filter, apply it to cached constructor
                if (mFilter != null) {
                    // Have we seen this name before?
                    Boolean allowedState = mFilterMap.get(name);
                    if (allowedState == null) {
                        // New class -- remember whether it is allowed
                        clazz = mContext.getClassLoader().loadClass(
                                prefix != null ? (prefix + name) : name).asSubclass(View.class);
                        
                        boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
                        mFilterMap.put(name, allowed);
                        if (!allowed) {
                            failNotAllowed(name, prefix, attrs);
                        }
                    } else if (allowedState.equals(Boolean.FALSE)) {
                        failNotAllowed(name, prefix, attrs);
                    }
                }
            }

            Object[] args = mConstructorArgs;
            args[1] = attrs;

            // 2. 创建对应的View或者ViewGroup对象。
            constructor.setAccessible(true);
            final View view = constructor.newInstance(args);
            if (view instanceof ViewStub) {
                // always use ourselves when inflating ViewStub later
                final ViewStub viewStub = (ViewStub) view;
                viewStub.setLayoutInflater(this);
            }
            return view;

        }
        ……
    }
posted @ 2014-05-27 09:34  Leslie Guan  阅读(1510)  评论(0编辑  收藏  举报