环境
本文以androidx,API 28代码来进行分析setContentView流程
先看一下AppCompatActivity中的onCreate方法代码,如下:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
final AppCompatDelegate delegate = getDelegate();
delegate.installViewFactory();
delegate.onCreate(savedInstanceState);
if (delegate.applyDayNight() && mThemeId != 0) {
// If DayNight has been applied, we need to re-apply the theme for
// the changes to take effect. On API 23+, we should bypass
// setTheme(), which will no-op if the theme ID is identical to the
// current theme ID.
if (Build.VERSION.SDK_INT >= 23) {
onApplyThemeResource(getTheme(), mThemeId, false);
} else {
setTheme(mThemeId);
}
}
super.onCreate(savedInstanceState);
}
其中getDelegate方法返回的是AppCompatDelegate 的实现类AppCompatDelegateImpl
@NonNull
public AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this);
}
return mDelegate;
}
public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
return new AppCompatDelegateImpl(activity, activity.getWindow(), callback);
}
activity.getWindow()对应的Window为PhoneWindow,在AppCompatDelegateImpl的构造方法里,主要做的事情是初始化变量,给window设置回调和背景
private static final int[] sWindowBackgroundStyleable = {android.R.attr.windowBackground};
AppCompatDelegateImpl(Context context, Window window, AppCompatCallback callback) {
mContext = context;
mWindow = window;
mAppCompatCallback = callback;
mOriginalWindowCallback = mWindow.getCallback();
if (mOriginalWindowCallback instanceof AppCompatWindowCallback) {
throw new IllegalStateException(
"AppCompat has already installed itself into the Window");
}
mAppCompatWindowCallback = new AppCompatWindowCallback(mOriginalWindowCallback);
// Now install the new callback
mWindow.setCallback(mAppCompatWindowCallback);
final TintTypedArray a = TintTypedArray.obtainStyledAttributes(
context, null, sWindowBackgroundStyleable);
final Drawable winBg = a.getDrawableIfKnown(0);
if (winBg != null) {
mWindow.setBackgroundDrawable(winBg);
}
a.recycle();
}
delegate.installViewFactory()是为LayoutInflater设置Factory2,拦截监听View的创建,代码如下:
public void installViewFactory() {
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
if (layoutInflater.getFactory() == null) {
LayoutInflaterCompat.setFactory2(layoutInflater, this);
} else {
if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImpl)) {
Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
+ " so we can not install AppCompat's");
}
}
}
public static void setFactory2(@NonNull LayoutInflater inflater, @NonNull LayoutInflater.Factory2 factory) {
inflater.setFactory2(factory);
if (Build.VERSION.SDK_INT < 21) {
final LayoutInflater.Factory f = inflater.getFactory();
if (f instanceof LayoutInflater.Factory2) {
// The merged factory is now set to getFactory(), but not getFactory2() (pre-v21).
// We will now try and force set the merged factory to mFactory2
forceSetFactory2(inflater, (LayoutInflater.Factory2) f);
} else {
// Else, we will force set the original wrapped Factory2
forceSetFactory2(inflater, factory);
}
}
}
/**
* Like {@link #setFactory}, but allows you to set a {@link Factory2}
* interface.
*/
public void setFactory2(Factory2 factory) {
if (mFactorySet) {
throw new IllegalStateException("A factory has already been set on this LayoutInflater");
}
if (factory == null) {
throw new NullPointerException("Given factory can not be null");
}
mFactorySet = true;
if (mFactory == null) {
mFactory = mFactory2 = factory;
} else {
mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
}
}
从代码中可以看出setFactory2方法只能设置调用一次,否则会报new IllegalStateException("A factory has already been set on this LayoutInflater")的异常,如果我们需要自定义Factory,需要在super.onCreate之前执行,因为系统在super.onCreate中会判断Factory是否为null,如果为null则会默认设置了一个Factory,我们再重复设置就会报错了。onCreate方法中还有适配夜间模式的代码,此处不再展开分析。
网友评论