美文网首页
Android setContentView()源码流程分析

Android setContentView()源码流程分析

作者: 曾大稳丶 | 来源:发表于2017-05-10 14:51 被阅读0次

    我们在Activity创建的时候,都用调用setContentView()函数来设置界面,下面我们通过源码来分析setContentView()的流程。
    我们先看Activity里面的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.
         *
         * @see #setContentView(android.view.View)
         * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
         */
        public void setContentView(@LayoutRes int layoutResID) {
            getWindow().setContentView(layoutResID);
            initWindowDecorActionBar();
        }
    

    函数如下:
    我们再次点击setContentView进去查看发现是一个Window的抽象方法,所以我们要找到对应的实现类,我们点击getWindow()进去查看最终得到:

     mWindow = new PhoneWindow(this, window);
    

    我们得到了他的实现类PhoneWindow,然后在PhoneWindow找到setContentView函数:

    @Override
        public void setContentView(int layoutResID) {
            // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
            // decor, when theme attributes and the like are crystalized. Do not check the feature
            // before this happens.
            if (mContentParent == null) {
                installDecor();
            } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                mContentParent.removeAllViews();
            }
    
            if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                        getContext());
                transitionTo(newScene);
            } else {
                mLayoutInflater.inflate(layoutResID, mContentParent);
            }
            mContentParent.requestApplyInsets();
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
        }
    
        @Override
        public void setContentView(View view) {
            setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
    
        @Override
        public void setContentView(View view, ViewGroup.LayoutParams params) {
            // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
            // decor, when theme attributes and the like are crystalized. Do not check the feature
            // before this happens.
            if (mContentParent == null) {
                installDecor();
            } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                mContentParent.removeAllViews();
            }
    
            if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                view.setLayoutParams(params);
                final Scene newScene = new Scene(mContentParent, view);
                transitionTo(newScene);
            } else {
                mContentParent.addView(view, params);
            }
            mContentParent.requestApplyInsets();
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
        }
    

    可以发现都是把这个View加到了mContentParent这个ViewGroup里面去了,这个mContentParent是什么呢?我们急着看源码,发现在installDecor()方法里面:

     mContentParent = generateLayout(mDecor);
    

    继续点进去:

    protected ViewGroup generateLayout(DecorView decor) {
            // Apply data from current theme.
    
            TypedArray a = getWindowStyle();
    
            if (false) {
                System.out.println("From style:");
                String s = "Attrs:";
                for (int i = 0; i < R.styleable.Window.length; i++) {
                    s = s + " " + Integer.toHexString(R.styleable.Window[i
    ![无标题.png](https://img.haomeiwen.com/i4658633/9a3a758cbac9f8aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    ]) + "="
                            + a.getString(i);
                }
                System.out.println(s);
            }
    
            //根据style属性做一些列的判断...
    
          //在做一些列的判断得到layoutResource
            layoutResource=.... //这里用R.layout.screen_simple来分析
           
            mDecor.startChanging();
    
            View in = mLayoutInflater.inflate(layoutResource, null);
            decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            mContentRoot = (ViewGroup) in;
    
            ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
            if (contentParent == null) {
                throw new RuntimeException("Window couldn't find content container view");
            }
      
           //......
    
            mDecor.finishChanging();
    
            return contentParent;
        }
    

    从这段代码可以知道,通过一系列的判断,得到相对于的layoutResource,然后通过mLayoutInflater.inflate(layoutResource, null);得到这个View,将其加入到mDecor,其中mContentParent最终为一个ID_ANDROID_CONTENT = com.android.internal.R.id.content的一个ViewGroup,在这里我们拿R.layout.screen_simple布局来看看

    <?xml version="1.0" encoding="utf-8"?>
    <!--
    /* //device/apps/common/assets/res/layout/screen_simple.xml
    **
    ** Copyright 2006, The Android Open Source Project
    **
    ** Licensed under the Apache License, Version 2.0 (the "License"); 
    ** you may not use this file except in compliance with the License. 
    ** You may obtain a copy of the License at 
    **
    **     http://www.apache.org/licenses/LICENSE-2.0 
    **
    ** Unless required by applicable law or agreed to in writing, software 
    ** distributed under the License is distributed on an "AS IS" BASIS, 
    ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
    ** See the License for the specific language governing permissions and 
    ** limitations under the License.
    */
    
    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:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        android:orientation="vertical">
        <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:id="@android:id/content"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:foregroundInsidePadding="false"
             android:foregroundGravity="fill_horizontal|top"
             android:foreground="?android:attr/windowContentOverlay" />
    </LinearLayout>
    
    

    可以知道这个mContentParent为一个FrameLayout,这时候我们应该知道,我们setContentView其实就是把那个View加入到一个idcom.android.internal.R.id.contentFrameLayout里面,而这个idcom.android.internal.R.id.contentFrameLayoutparentView又是加在mDecor里面,我们来看看这个mDecor是什么,在installDecor()函数中:

          if (mDecor == null) {
                mDecor = generateDecor();
                mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
                mDecor.setIsRootNamespace(true);
                if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                    mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
                }
            }
    
     protected DecorView generateDecor() {
            return new DecorView(getContext(), -1);
        }
    

    可以发现这个mDecor就是一个new的一个DecorView,我们继续看:

    private final class DecorView extends FrameLayout
    

    这个DecorView其实也是一个FrameLayout,这个时候我们就可以得到这样一张图:

    在这基础上我们看AppCompatActivitysetContentView函数:

        @Override
        public void setContentView(@LayoutRes int layoutResID) {
            getDelegate().setContentView(layoutResID);
        }
    
    
        /**
         * @return The {@link AppCompatDelegate} being used by this Activity.
         */
        @NonNull
        public AppCompatDelegate getDelegate() {
            if (mDelegate == null) {
                mDelegate = AppCompatDelegate.create(this, this);
            }
            return mDelegate;
        }
    
      /**
         * Create a {@link android.support.v7.app.AppCompatDelegate} to use with {@code activity}.
         *
         * @param callback An optional callback for AppCompat specific events
         */
        public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
            return create(activity, activity.getWindow(), callback);
        }
       private static AppCompatDelegate create(Context context, Window window,
                AppCompatCallback callback) {
            final int sdk = Build.VERSION.SDK_INT;
            if (sdk >= 23) {
                return new AppCompatDelegateImplV23(context, window, callback);
            } else if (sdk >= 14) {
                return new AppCompatDelegateImplV14(context, window, callback);
            } else if (sdk >= 11) {
                return new AppCompatDelegateImplV11(context, window, callback);
            } else {
                return new AppCompatDelegateImplV7(context, window, callback);
            }
        }
    

    可以看到最终进入到了AppCompatDelegatecreate方法,这个函数通过new 23 14 11 7就可以看出是为了兼容不同的版本,我们点进去就可以看到AppCompatDelegateImplV23-->AppCompatDelegateImplV14-->AppCompatDelegateImplV11-->AppCompatDelegateImplV7依次继承的,我们最终查看到AppCompatDelegateImplV7setContentView函数:

    @Override
        public void setContentView(View v) {
            ensureSubDecor();
            ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
            contentParent.removeAllViews();
            contentParent.addView(v);
            mOriginalWindowCallback.onContentChanged();
        }
    
        @Override
        public void setContentView(int resId) {
            ensureSubDecor();
            ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
            contentParent.removeAllViews();
            LayoutInflater.from(mContext).inflate(resId, contentParent);
            mOriginalWindowCallback.onContentChanged();
        }
    
        @Override
        public void setContentView(View v, ViewGroup.LayoutParams lp) {
            ensureSubDecor();
            ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
            contentParent.removeAllViews();
            contentParent.addView(v, lp);
            mOriginalWindowCallback.onContentChanged();
        }
    

    其实也就是把得到的view添加到contentParent里面。
    比如说,在android21以前一般是使用控件TextView等控件,在21以后出了相关的AppCompat控件,这个时候怎么让开发者写的TextView自动转换为AppCompatTextView呢?所以在AppCompatDelegateImplV7重写了这样一个函数函数:

     @Override
        public void installViewFactory() {
            LayoutInflater layoutInflater = LayoutInflater.from(mContext);
            if (layoutInflater.getFactory() == null) {
                LayoutInflaterCompat.setFactory(layoutInflater, this);
            } else {
                if (!(LayoutInflaterCompat.getFactory(layoutInflater)
                        instanceof AppCompatDelegateImplV7)) {
                    Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
                            + " so we can not install AppCompat's");
                }
            }
        }
    
    

    我们查找abstract类,可以查看着方法注释:

    /**
         * Installs AppCompat's {@link android.view.LayoutInflater} Factory so that it can replace
         * the framework widgets with compatible tinted versions. This should be called before
         * {@code super.onCreate()} as so:
         * <pre class="prettyprint">
         * protected void onCreate(Bundle savedInstanceState) {
         *     getDelegate().installViewFactory();
         *     getDelegate().onCreate(savedInstanceState);
         *     super.onCreate(savedInstanceState);
         *
         *     // ...
         * }
         * </pre>
         * If you are using your own {@link android.view.LayoutInflater.Factory Factory} or
         * {@link android.view.LayoutInflater.Factory2 Factory2} then you can omit this call, and instead call
         * {@link #createView(android.view.View, String, android.content.Context, android.util.AttributeSet)}
         * from your factory to return any compatible widgets.
         */
        public abstract void installViewFactory();
    

    从意思可以看出,就是说我们可以通过这个方法然后给LayoutInflater设置一个Factory,这个Factory是干嘛的呢?从我的上篇文章就知道,这个Factory是在LayoutInflater执行inflate函数生成View的时候用的,这个Factory可以拦截View的生成,通过这个Factory我们可以自己给inflate写一套解析layout.xml的规则,在换肤的时候就可以用到这个。我们实现LayoutInflaterFactory接口,重写onCreateView方法,就可以拦截相应的信息进行解析。比如在AppCompatDelegateImplV7类中:

     /**
         * From {@link android.support.v4.view.LayoutInflaterFactory}
         */
        @Override
        public final View onCreateView(View parent, String name,
                Context context, AttributeSet attrs) {
            // First let the Activity's Factory try and inflate the view
            final View view = callActivityOnCreateView(parent, name, context, attrs);
            if (view != null) {
                return view;
            }
    
            // If the Factory didn't handle it, let our createView() method try
            return createView(parent, name, context, attrs);
        }
    
    
          View callActivityOnCreateView(View parent, String name, Context context, AttributeSet attrs) {
            // Let the Activity's LayoutInflater.Factory try and handle it
            if (mOriginalWindowCallback instanceof LayoutInflater.Factory) {
                final View result = ((LayoutInflater.Factory) mOriginalWindowCallback)
                        .onCreateView(name, context, attrs);
                if (result != null) {
                    return result;
                }
            }
            return null;
        }
    
    @Override
        public View createView(View parent, final String name, @NonNull Context context,
                @NonNull AttributeSet attrs) {
            final boolean isPre21 = Build.VERSION.SDK_INT < 21;
    
            if (mAppCompatViewInflater == null) {
                mAppCompatViewInflater = new AppCompatViewInflater();
            }
    
            // We only want the View to inherit its context if we're running pre-v21
            final boolean inheritContext = isPre21 && shouldInheritContext((ViewParent) parent);
    
            return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
                    isPre21, /* Only read android:theme pre-L (L+ handles this anyway) */
                    true /* Read read app:theme as a fallback at all times for legacy reasons */
            );
        }
    

    它拦截下了layoutt.xml的解析,自己写了一个解析类AppCompatViewInflater,来解析View

    /*
     * Copyright (C) 2014 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package android.support.v7.app;
    
    import android.content.Context;
    import android.content.ContextWrapper;
    import android.content.res.TypedArray;
    import android.os.Build;
    import android.support.annotation.NonNull;
    import android.support.annotation.Nullable;
    import android.support.v4.util.ArrayMap;
    import android.support.v4.view.ViewCompat;
    import android.support.v7.appcompat.R;
    import android.support.v7.view.ContextThemeWrapper;
    import android.support.v7.widget.AppCompatAutoCompleteTextView;
    import android.support.v7.widget.AppCompatButton;
    import android.support.v7.widget.AppCompatCheckBox;
    import android.support.v7.widget.AppCompatCheckedTextView;
    import android.support.v7.widget.AppCompatEditText;
    import android.support.v7.widget.AppCompatImageButton;
    import android.support.v7.widget.AppCompatImageView;
    import android.support.v7.widget.AppCompatMultiAutoCompleteTextView;
    import android.support.v7.widget.AppCompatRadioButton;
    import android.support.v7.widget.AppCompatRatingBar;
    import android.support.v7.widget.AppCompatSeekBar;
    import android.support.v7.widget.AppCompatSpinner;
    import android.support.v7.widget.AppCompatTextView;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.InflateException;
    import android.view.View;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.Map;
    
    /**
     * This class is responsible for manually inflating our tinted widgets which are used on devices
     * running {@link android.os.Build.VERSION_CODES#KITKAT KITKAT} or below. As such, this class
     * should only be used when running on those devices.
     * <p>This class two main responsibilities: the first is to 'inject' our tinted views in place of
     * the framework versions in layout inflation; the second is backport the {@code android:theme}
     * functionality for any inflated widgets. This include theme inheritance from it's parent.
     */
    class AppCompatViewInflater {
    
        private static final Class<?>[] sConstructorSignature = new Class[]{
                Context.class, AttributeSet.class};
        private static final int[] sOnClickAttrs = new int[]{android.R.attr.onClick};
    
        private static final String[] sClassPrefixList = {
                "android.widget.",
                "android.view.",
                "android.webkit."
        };
    
        private static final String LOG_TAG = "AppCompatViewInflater";
    
        private static final Map<String, Constructor<? extends View>> sConstructorMap
                = new ArrayMap<>();
    
        private final Object[] mConstructorArgs = new Object[2];
    
        public final View createView(View parent, final String name, @NonNull Context context,
                @NonNull AttributeSet attrs, boolean inheritContext,
                boolean readAndroidTheme, boolean readAppTheme) {
            final Context originalContext = context;
    
            // We can emulate Lollipop's android:theme attribute propagating down the view hierarchy
            // by using the parent's context
            if (inheritContext && parent != null) {
                context = parent.getContext();
            }
            if (readAndroidTheme || readAppTheme) {
                // We then apply the theme on the context, if specified
                context = themifyContext(context, attrs, readAndroidTheme, readAppTheme);
            }
    
            View view = null;
    
            // We need to 'inject' our tint aware Views in place of the standard framework versions
            switch (name) {
                case "TextView":
                    view = new AppCompatTextView(context, attrs);
                    break;
                case "ImageView":
                    view = new AppCompatImageView(context, attrs);
                    break;
                case "Button":
                    view = new AppCompatButton(context, attrs);
                    break;
                case "EditText":
                    view = new AppCompatEditText(context, attrs);
                    break;
                case "Spinner":
                    view = new AppCompatSpinner(context, attrs);
                    break;
                case "ImageButton":
                    view = new AppCompatImageButton(context, attrs);
                    break;
                case "CheckBox":
                    view = new AppCompatCheckBox(context, attrs);
                    break;
                case "RadioButton":
                    view = new AppCompatRadioButton(context, attrs);
                    break;
                case "CheckedTextView":
                    view = new AppCompatCheckedTextView(context, attrs);
                    break;
                case "AutoCompleteTextView":
                    view = new AppCompatAutoCompleteTextView(context, attrs);
                    break;
                case "MultiAutoCompleteTextView":
                    view = new AppCompatMultiAutoCompleteTextView(context, attrs);
                    break;
                case "RatingBar":
                    view = new AppCompatRatingBar(context, attrs);
                    break;
                case "SeekBar":
                    view = new AppCompatSeekBar(context, attrs);
                    break;
            }
    
            if (view == null && originalContext != context) {
                // If the original context does not equal our themed context, then we need to manually
                // inflate it using the name so that android:theme takes effect.
                view = createViewFromTag(context, name, attrs);
            }
    
            if (view != null) {
                // If we have created a view, check it's android:onClick
                checkOnClickListener(view, attrs);
            }
    
            return view;
        }
    
        private View createViewFromTag(Context context, String name, AttributeSet attrs) {
            if (name.equals("view")) {
                name = attrs.getAttributeValue(null, "class");
            }
    
            try {
                mConstructorArgs[0] = context;
                mConstructorArgs[1] = attrs;
    
                if (-1 == name.indexOf('.')) {
                    for (int i = 0; i < sClassPrefixList.length; i++) {
                        final View view = createView(context, name, sClassPrefixList[i]);
                        if (view != null) {
                            return view;
                        }
                    }
                    return null;
                } else {
                    return createView(context, name, null);
                }
            } catch (Exception e) {
                // We do not want to catch these, lets return null and let the actual LayoutInflater
                // try
                return null;
            } finally {
                // Don't retain references on context.
                mConstructorArgs[0] = null;
                mConstructorArgs[1] = null;
            }
        }
    
        /**
         * android:onClick doesn't handle views with a ContextWrapper context. This method
         * backports new framework functionality to traverse the Context wrappers to find a
         * suitable target.
         */
        private void checkOnClickListener(View view, AttributeSet attrs) {
            final Context context = view.getContext();
    
            if (!(context instanceof ContextWrapper) ||
                    (Build.VERSION.SDK_INT >= 15 && !ViewCompat.hasOnClickListeners(view))) {
                // Skip our compat functionality if: the Context isn't a ContextWrapper, or
                // the view doesn't have an OnClickListener (we can only rely on this on API 15+ so
                // always use our compat code on older devices)
                return;
            }
    
            final TypedArray a = context.obtainStyledAttributes(attrs, sOnClickAttrs);
            final String handlerName = a.getString(0);
            if (handlerName != null) {
                view.setOnClickListener(new DeclaredOnClickListener(view, handlerName));
            }
            a.recycle();
        }
    
        private View createView(Context context, String name, String prefix)
                throws ClassNotFoundException, InflateException {
            Constructor<? extends View> constructor = sConstructorMap.get(name);
    
            try {
                if (constructor == null) {
                    // Class not found in the cache, see if it's real, and try to add it
                    Class<? extends View> clazz = context.getClassLoader().loadClass(
                            prefix != null ? (prefix + name) : name).asSubclass(View.class);
    
                    constructor = clazz.getConstructor(sConstructorSignature);
                    sConstructorMap.put(name, constructor);
                }
                constructor.setAccessible(true);
                return constructor.newInstance(mConstructorArgs);
            } catch (Exception e) {
                // We do not want to catch these, lets return null and let the actual LayoutInflater
                // try
                return null;
            }
        }
    
        /**
         * Allows us to emulate the {@code android:theme} attribute for devices before L.
         */
        private static Context themifyContext(Context context, AttributeSet attrs,
                boolean useAndroidTheme, boolean useAppTheme) {
            final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.View, 0, 0);
            int themeId = 0;
            if (useAndroidTheme) {
                // First try reading android:theme if enabled
                themeId = a.getResourceId(R.styleable.View_android_theme, 0);
            }
            if (useAppTheme && themeId == 0) {
                // ...if that didn't work, try reading app:theme (for legacy reasons) if enabled
                themeId = a.getResourceId(R.styleable.View_theme, 0);
    
                if (themeId != 0) {
                    Log.i(LOG_TAG, "app:theme is now deprecated. "
                            + "Please move to using android:theme instead.");
                }
            }
            a.recycle();
    
            if (themeId != 0 && (!(context instanceof ContextThemeWrapper)
                    || ((ContextThemeWrapper) context).getThemeResId() != themeId)) {
                // If the context isn't a ContextThemeWrapper, or it is but does not have
                // the same theme as we need, wrap it in a new wrapper
                context = new ContextThemeWrapper(context, themeId);
            }
            return context;
        }
    
        /**
         * An implementation of OnClickListener that attempts to lazily load a
         * named click handling method from a parent or ancestor context.
         */
        private static class DeclaredOnClickListener implements View.OnClickListener {
            private final View mHostView;
            private final String mMethodName;
    
            private Method mResolvedMethod;
            private Context mResolvedContext;
    
            public DeclaredOnClickListener(@NonNull View hostView, @NonNull String methodName) {
                mHostView = hostView;
                mMethodName = methodName;
            }
    
            @Override
            public void onClick(@NonNull View v) {
                if (mResolvedMethod == null) {
                    resolveMethod(mHostView.getContext(), mMethodName);
                }
    
                try {
                    mResolvedMethod.invoke(mResolvedContext, v);
                } catch (IllegalAccessException e) {
                    throw new IllegalStateException(
                            "Could not execute non-public method for android:onClick", e);
                } catch (InvocationTargetException e) {
                    throw new IllegalStateException(
                            "Could not execute method for android:onClick", e);
                }
            }
    
            @NonNull
            private void resolveMethod(@Nullable Context context, @NonNull String name) {
                while (context != null) {
                    try {
                        if (!context.isRestricted()) {
                            final Method method = context.getClass().getMethod(mMethodName, View.class);
                            if (method != null) {
                                mResolvedMethod = method;
                                mResolvedContext = context;
                                return;
                            }
                        }
                    } catch (NoSuchMethodException e) {
                        // Failed to find method, keep searching up the hierarchy.
                    }
    
                    if (context instanceof ContextWrapper) {
                        context = ((ContextWrapper) context).getBaseContext();
                    } else {
                        // Can't search up the hierarchy, null out and fail.
                        context = null;
                    }
                }
    
                final int id = mHostView.getId();
                final String idText = id == View.NO_ID ? "" : " with id '"
                        + mHostView.getContext().getResources().getResourceEntryName(id) + "'";
                throw new IllegalStateException("Could not find method " + mMethodName
                        + "(View) in a parent or ancestor Context for android:onClick "
                        + "attribute defined on view " + mHostView.getClass() + idText);
            }
        }
    }
    
    

    这样就达到了将以前的TextView等转换为相关的AppCompat控件,达到兼容。

    setContentView()源码流程就分析到这里,细看请自行查看源码。

    相关文章

      网友评论

          本文标题:Android setContentView()源码流程分析

          本文链接:https://www.haomeiwen.com/subject/tolitxtx.html