Android AppCompatActivity

简述

在很久很久以前,android的activity时继承Activity的,但在后来却改成了AppCompatActivity,那AppCompatActivity是什么呢?继承他有什么用呢?
其实他最主要的工作是5.0之后的View兼容和页面主题相关处理,例如AppCompatTextView之类的内部都是有兼容操作的,而继承AppCompatActivity后,其会在内部将TextView替换为AppCompatTextView(之前一直在想使用TextView好还是AppCompatTextView好,其实都一样,其内部会进行替换),那这里就来分析下它是怎么进行替换的.

分析

Factory

首先来说说Factory

public interface Factory {
        public View onCreateView(String name, Context context, AttributeSet attrs);
    }

    public interface Factory2 extends Factory {
        public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
    }

Factory的作用是对View的创建,在Activity创建View时会调用其Factory进行创建,其调用过程是

Activyty
|
setContentView()
|
PhoneView.setContentView()
|
LayoutInflater.inflate()
|
createViewFromTag()

createViewFromTag的实现为

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        ...
        try {
            View view;
            if (mFactory2 != null) {//检查是否设置的Factory2,有就调用其来创建View
                view = mFactory2.onCreateView(parent, name, context, attrs);
            } else if (mFactory != null) {
                view = mFactory.onCreateView(name, context, attrs);
            } else {
                view = null;
            }

            if (view == null && mPrivateFactory != null) {//mPrivateFactory 是内部的工厂,activity中的实现为空实现
                view = mPrivateFactory.onCreateView(parent, name, context, attrs);
            }

            if (view == null) {//此处的View未建立则会通过反射来进行创建
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(parent, name, attrs);
                    } else {
                        view = createView(name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }

            return view;
        } ...//一系列异常处理
    }

由此可见,使用Factory可以对所有的View的创建进行拦截,并自定义其创建方式

AppCompatActivity的onCreate

AppCompatActivity的替换操作主要在onCreate进行,这里主要做的是设置Factory2(其继承了Factory,所以内部需要实现两个创建View的方法)

 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
       ...
        delegate.installViewFactory();//装载Factory2
        ...
        super.onCreate(savedInstanceState);
    }

而具体的装载过程在AppCompatDelegateImplV9中

@Override
    public void installViewFactory() {
        LayoutInflater layoutInflater = LayoutInflater.from(mContext);
        if (layoutInflater.getFactory() == null) {
            LayoutInflaterCompat.setFactory2(layoutInflater, this);//设置View创建工厂
        } else {
            if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImplV9)) {
                Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
                        + " so we can not install AppCompat's");
            }
        }
    }

可以看出AppCompatActivity通过setFactory2来设置,而且设置前会检查时候已经设置,如果已经设置了就会报错.
其Factory的实现为

/**
     * From {@link LayoutInflater.Factory2}.
     */
    @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);//此处是调用Activity中的onCreateView方法
        if (view != null) {
            return view;
        }

        // If the Factory didn't handle it, let our createView() method try
        return createView(parent, name, context, attrs);//此处进行兼容性View的替换
    }

    /**
     * From {@link LayoutInflater.Factory2}.
     */
    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        return onCreateView(null, name, context, attrs);//直接调用上面的方法
    }

在createView的一系列的操作后会进行兼容性View的替换,具体实现在AppCompatViewInflater中

final View createView(View parent, final String name, @NonNull Context context,
            @NonNull AttributeSet attrs, boolean inheritContext,
            boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
        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);
        }
        if (wrapContext) {
            context = TintContextWrapper.wrap(context);
        }

        View view = null;

        // We need to 'inject' our tint aware Views in place of the standard framework versions
        switch (name) {//创建兼容性View
            case "TextView":
                view = createTextView(context, attrs);//返回AppCompatTextView,以下类似
                verifyNotNull(view, name);//空监测,为空会直接抛异常
                break;
            case "ImageView":
                view = createImageView(context, attrs);
                verifyNotNull(view, name);
                break;
            case "Button":
                view = createButton(context, attrs);
                verifyNotNull(view, name);
                break;
            case "EditText":
                view = createEditText(context, attrs);
                verifyNotNull(view, name);
                break;
            case "Spinner":
                view = createSpinner(context, attrs);
                verifyNotNull(view, name);
                break;
            case "ImageButton":
                view = createImageButton(context, attrs);
                verifyNotNull(view, name);
                break;
            case "CheckBox":
                view = createCheckBox(context, attrs);
                verifyNotNull(view, name);
                break;
            case "RadioButton":
                view = createRadioButton(context, attrs);
                verifyNotNull(view, name);
                break;
            case "CheckedTextView":
                view = createCheckedTextView(context, attrs);
                verifyNotNull(view, name);
                break;
            case "AutoCompleteTextView":
                view = createAutoCompleteTextView(context, attrs);
                verifyNotNull(view, name);
                break;
            case "MultiAutoCompleteTextView":
                view = createMultiAutoCompleteTextView(context, attrs);
                verifyNotNull(view, name);
                break;
            case "RatingBar":
                view = createRatingBar(context, attrs);
                verifyNotNull(view, name);
                break;
            case "SeekBar":
                view = createSeekBar(context, attrs);
                verifyNotNull(view, name);
                break;
            default:
                // The fallback that allows extending class to take over view inflation
                // for other tags. Note that we don't check that the result is not-null.
                // That allows the custom inflater path to fall back on the default one
                // later in this method.
                view = createView(context, name, attrs);//这里是空实现
        }

        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 its android:onClick
            checkOnClickListener(view, attrs);
        }

        return view;
    }

由此可见,在创建View 时AppCompatActivity将TextView创建为AppCompatTextView

End

综上所诉,AppCompatActivity通过Factory将View替换为具有兼容性的View,当然AppCompatActivity其中也很多其他的操作,这里没有详细去说明,若文中有什么错误的请指出纠正,谢谢哟~~~

你可能感兴趣的:(Android AppCompatActivity)