Activity setContentView源码阅读分析

以下以Android api28 为例
1. Activity ContentView 分析

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

那我们就要来根据源码来分析setContentView 做了什么事
1.1 Activity -> setContentView

  public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
  public Window getWindow() {
        return mWindow;
    }
  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, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) { 
            ....
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        ....
        }

1.2 从源码看 mWindow 对应的实现类 PhoneWindow

PhoneWindow 里面的setContentView(R.layout.activity_main)

 public void setContentView(int layoutResID) {
        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 {
           //加载 contentView 传入的activity_main 至 mContentParent  视图上
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

生成decorView 视图 ,获取id为ID_ANDROID_CONTENT的FrameLayout的ViewGroup视图

ID_ANDROID_CONTENT 为R.id.content;

	private void installDecor(){
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1); // 创建DecorView
         }
          if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
		  }	
	  }
	  
	   protected DecorView generateDecor(int featureId) {
        ...
        //返回 new 了一个 DecorView
        return new DecorView(context, featureId, this, getAttributes()); 
    }
protected ViewGroup generateLayout(DecorView decor) {
 	int layoutResource;
 	//一系列的判断,去加载系统布局
 	 mDecor.startChanging();
 	 //去加载系统的 layoutResource
     mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
     ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    ....
     mDecor.setWindowBackground(background);
     
     mDecor.finishChanging();

     return contentParent;
}
//添加系统布局到 DecorView 
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {\
        mDecorCaptionView = createDecorCaptionView(inflater);
        final View root = inflater.inflate(layoutResource, null);
        if (mDecorCaptionView != null) {
            if (mDecorCaptionView.getParent() == null) {
                addView(mDecorCaptionView,
                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            }
            mDecorCaptionView.addView(root,
                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
        } else {
            // Put it below the color views.
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mContentRoot = (ViewGroup) root;
        initializeElevation();
    }

1.3 视图的整体布局
PhoneWindow
      ->DectorView
            ->系统布局
                 R.id.content fragment
                       ->自己调用setContentView 的布局

2. AppCompateActivity setContentView 分析

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

}

2.1 AppCompatActivity setContentView的源码

  public void setContentView(@LayoutRes int layoutResID) {
        this.getDelegate().setContentView(layoutResID);
    }
   public AppCompatDelegate getDelegate() {
        if (this.mDelegate == null) {
            this.mDelegate = AppCompatDelegate.create(this, this);
        }

        return this.mDelegate;
    }

	public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
        return new AppCompatDelegateImpl(activity, activity.getWindow(), callback);
    }

2.2 AppCompatDelegateImpl setContentView的源码

public void setContentView(int resId) {
        this.ensureSubDecor();
        ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290);
        contentParent.removeAllViews();
        LayoutInflater.from(this.mContext).inflate(resId, contentParent);
        this.mOriginalWindowCallback.onContentChanged();
    }

通过debug 调试,我们发现 extends Activity 跟 AppCompatActivity 的区别

//extends Activity mImageIV是 ImageView
//而 extends AppCompatActivity mImageIV 是 AppCompatImageView 
Log.e("TAG",""+mImageIv) ;

问题引出
为什么extends AppCompatActivity 的ImageView 会被替换成 AppCompatImageView 呢?
AppCompatActivity 会对View进行拦截,替换成自己的View

原因出在LayoutInflater 来inflater 我们的布局
我们现在来看看 LayoutInflater 的源码

3. LayoutInflater inflater 的源码

3.1 LayoutInflater.from(this) 获取的源码

    public static LayoutInflater from(Context context) {
        LayoutInflater LayoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        return LayoutInflater;
    }
	@Override
	public Object getSystemService(String name){
		return SystemSericeRegistry.getSystemService(this,name);
	}
ServiceFetcher<?> fetcher = SYSTEM_SERVICES_FETCHERS.get(name)://从静态的map集合获取

LayoutInflater.from(this) 它是系统服务,是单例设计模式。

3.1 实例化View的inflater方法的源码

  //方法2
 	public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);
    }
     //方法3
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                    + Integer.toHexString(resource) + ")");
        }

        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
         ...   
      final View temp = createViewFromTag(root, name, inflaterContext, attrs);
		...
            return result;
        }
    }
 View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
      ...
        try {
            View view;
            if (mFactory2 != null) {
                view = mFactory2.onCreateView(parent, name, context, attrs);
            } else if (mFactory != null) {
                view = mFactory.onCreateView(name, context, attrs);
            } else {
                view = null;
            }
         ....
    }

3.2 从这里可以看出是mFactory2 起了作用,再来看看什么时候设置了mFactory2
现在回到 AppCompatActivity oncreate方法 可以看到
delegate.installViewFactory(); 设置Factory2

   protected void onCreate(@Nullable Bundle savedInstanceState) {
        AppCompatDelegate delegate = this.getDelegate();
        delegate.installViewFactory();
        delegate.onCreate(savedInstanceState);
        if (delegate.applyDayNight() && this.mThemeId != 0) {
            if (VERSION.SDK_INT >= 23) {
                this.onApplyThemeResource(this.getTheme(), this.mThemeId, false);
            } else {
                this.setTheme(this.mThemeId);
            }
        }

        super.onCreate(savedInstanceState);
    }

installViewFactory方法

 public void installViewFactory() {
        LayoutInflater layoutInflater = LayoutInflater.from(this.mContext);
        if (layoutInflater.getFactory() == null) {
            LayoutInflaterCompat.setFactory2(layoutInflater, this);
        } 
    }
public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
        return this.createView(parent, name, context, attrs);
    }

createView方法

public View createView(View parent, String name, @NonNull Context context, @NonNull AttributeSet attrs) {
        ...
        //创建 mAppCompatViewInflater对象
        return this.mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext, IS_PRE_LOLLIPOP, true, VectorEnabledTintResources.shouldBeUsed());
    }

3.3 拦截View替换的 AppCompatViewInflater createView 源码

final View createView(View parent, String name, @NonNull Context context, @NonNull AttributeSet attrs, boolean inheritContext, boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
        Context originalContext = context;
        if (inheritContext && parent != null) {
            context = parent.getContext();
        }

        if (readAndroidTheme || readAppTheme) {
            context = themifyContext(context, attrs, readAndroidTheme, readAppTheme);
        }

        if (wrapContext) {
            context = TintContextWrapper.wrap(context);
        }

        View view = null;
        byte var12 = -1;
        switch(name.hashCode()) {
        case -1946472170:
            if (name.equals("RatingBar")) {
                var12 = 11;
            }
            break;
        case -1455429095:
            if (name.equals("CheckedTextView")) {
                var12 = 8;
            }
            break;
        case -1346021293:
            if (name.equals("MultiAutoCompleteTextView")) {
                var12 = 10;
            }
            break;
        case -938935918:
            if (name.equals("TextView")) {
                var12 = 0;
            }
            break;
        case -937446323:
            if (name.equals("ImageButton")) {
                var12 = 5;
            }
            break;
        case -658531749:
            if (name.equals("SeekBar")) {
                var12 = 12;
            }
            break;
        case -339785223:
            if (name.equals("Spinner")) {
                var12 = 4;
            }
            break;
        case 776382189:
            if (name.equals("RadioButton")) {
                var12 = 7;
            }
            break;
        case 1125864064:
            if (name.equals("ImageView")) {
                var12 = 1;
            }
            break;
        case 1413872058:
            if (name.equals("AutoCompleteTextView")) {
                var12 = 9;
            }
            break;
        case 1601505219:
            if (name.equals("CheckBox")) {
                var12 = 6;
            }
            break;
        case 1666676343:
            if (name.equals("EditText")) {
                var12 = 3;
            }
            break;
        case 2001146706:
            if (name.equals("Button")) {
                var12 = 2;
            }
        }

        switch(var12) {
        case 0:
            view = this.createTextView(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 1:
            view = this.createImageView(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 2:
            view = this.createButton(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 3:
            view = this.createEditText(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 4:
            view = this.createSpinner(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 5:
            view = this.createImageButton(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 6:
            view = this.createCheckBox(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 7:
            view = this.createRadioButton(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 8:
            view = this.createCheckedTextView(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 9:
            view = this.createAutoCompleteTextView(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 10:
            view = this.createMultiAutoCompleteTextView(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 11:
            view = this.createRatingBar(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 12:
            view = this.createSeekBar(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        default:
            view = this.createView(context, name, attrs);
        }

        if (view == null && originalContext != context) {
            view = this.createViewFromTag(context, name, attrs);
        }

        if (view != null) {
            this.checkOnClickListener((View)view, attrs);
        }

        return (View)view;
    }

到此为止,大家应该清楚contentView的流程了吧,不清楚的自己可以再走一边源码


通过上面的源码浏览 大家应该知道如何去 通过Factory拦截View,进行对View的二次加工

LayoutInflater.from(this).setFactory(new LayoutInflater.Factory() {
            @Override
            public View onCreateView(String name, Context context, AttributeSet attrs) {
                return null;
            }
        });
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值