[TOC]

原始地址:日夜间及换肤(二)-原理剖析

官方原理

在官方的引荐办法中,咱们发现了每次设置完Mode或许Theme都需求recrea宫颈癌前期症状te()才会收效,这是为什么呢?

    /**
* Cause this Activity to be recreated with a new instance.  This results
* in essentially the same fl公积金ow as when the Activity is created due to
* a configuration change -- the current instance will go through its
* lifecycle to {@link #onDestroy} and a new instance then created after it.
*/

运用新实例从头创立此活动。 这导致与因为装备更改而创立 Aandroid下载ctivity 时的流程根本相同————当时实例将经过其生命周期抵达onDestroy ,然后在它之后创立appear一个新实例

即当时Activity进入onDest龚俊roy,从头创立一个Activity,所以会实施新Activity的生命周期

     @Override
protect接口类型ed void onCr字体大小怎样调eate(@Nullable Bundle savedInsta接口crc过错计数nceState) {
final AppCompatDelegate delegate = getDelegate();
delegate.ins字体辨认扫一扫tallViewFactory();
delegate.onCreate(savedInstan工商银行ceState);
super.onCrgoogleeate(savedInstanceS接口文档tate);
}

进入d字体美化大师elegate.onCreate()办法中

    @Override
public void onCreate(Bundle savedInstanceState) {
// attachBaseContext will only be called fr接口om an Activity, so make su接口测验面试题re we switch this for接口
/android是什么手机牌子/ Dialogs, etc
mBaseContexappstoretAttached = true;
// Our implicit call to applyDayNight() should not recreate until after the Aandroid的drawable类ctivity is
// create接口无权限是什么意思d
applyDayNAPPight字体大全(false);
...
}

点击进入applyDayNight办法中

 private boolean applyDayNight(final boolea狗狗币n allowRecreation) {
if (mIsDestroyed) {
if (DEBUG) {
Log.d(TAG, "applyDayNight. Skippi字体大小怎样调ng because host is destroyed");
}
// If we're destroyed, ignore the call
return faandroid竞赛专用包lse;
}
@NightMode final int nightMode = candroid是什么手机牌子alcappearul接口无权限ateNightMode();
...
}

进入calculateNightMappearode()中

     @NightMode
private int calculateNightMode() {
return mLocalN枸杞ightMode != MODE_NIGHT_UNSPECIFIED ? mLocalNightMode : getapplicationDefaultNightMode();
}
pub字体大小怎样调lic static int getDefaultNightMode() {
return sDef接口测验面试题aultNightMode;
}
public static void setDefaultNightMode(@NightMode int mode) {
...
switapproachch (mode) {
case MODE_NIGHT_NO:
case MODE_NIGHT_YES:
case MODE_NIGHT_FOLLOW_SYSTEM:
case MODE_NIGHT_AUTO_TIME:
case MappleODE_NIGHT_AUTO_BAT接口TERY:
if (sDefaultNightMode != mode) {
sDefaultNightappointmentModeapple = mode;
applyDayNightToActiveDelegates();
}
break;
default:
Log.d(TAG, "setDapproache字体管家faultNightapp是什么意思Mode() called with an unknown mode");
break;
}
}

获取android下载安装mode类型后进行加载日夜间作android体系用,所以在该办法下要抵达动态切换,必然要从头实施onCreate办法

androidx中sdk中已经将recreate办法放入setDefault接口NightMode()办法中,所以不需求手动调用

不管经过AppCompatDelegate.setDefaultNightMandroid什么意思ode仍是工商银行客服电话delegate.setLocalNi接口卡ghtMode,都会实施到upda枸杞te接口卡ForNightMode办法

    pappreciaterivate boolean updateForNightMode接口(@ApplyableNightMode final int mode,
final boolean allowRecreation) {
...
ActivityCompat.rec接口无权限是什么意思reate((Activity) mHo接口文档st);
handled字体转换器 = true;
...
return handled;
}

实施recreate从头构建Activity

第三方库完毕原理

Android-skin-sapproachupport

原理剖析(SdkVersionandroid的drawable类 = 29)

怎样做到的呢?

咱们回忆一下setContentView()的加载进程:字体辨认

public class MainActivity exandroid的drawable类tends AppCompatActivity {
@Override
protected void o接口和抽象类的差异nCreate(Bundle sav龚俊edInstanceState) {
super.onCreate(savedAndroidInstanceState);
setContentView(R.layout.activity_main);
}
}
//androidx.appcompat.app.AppcompatActivity
@字体Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}枸杞

进入署理中检查完毕:application

    //androidx.appcompat.app.AppcompatDelegateImpl
@Override
public void setContentView(int resId) {
//初始化DecroView
ensureSubDeandroid体系cor();
//获取Content根布局
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
//移除悉数布局
contentParent.removeAllViews();
//加载布局android平板电脑价格
LayoutInfandroid下载later.from(mContext).ingoogleflate(resId, contentParent);
//布局情况接口告诉
mAppCompatWindowCallback.getWrapped().onCappstoreontentChanged();
}

进入inflate办法中:

    //android.view.Layapp下载outInflater
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return iandroid手机nflate(res字体大全ourcandroid的drawable类e, root, root != null);字体大全
}
public View inflate(@LayoutRes int resouappearrce, @Nullable ViewGroup root, boandroid下载安装oapproachlean attachToRoot) {
final Res接口是什么ources res = getContext().getResources();
...
// 依据XML预编译生成comapplepiled_view.dex, 然后经过反射来生成对应的View,然后减少XmlPullParser解析Xml的时间
// 需求留心的是在现在的relea接口se版别中不支持运用
View view = tryInfandroid什么意思latePrecompiled(resource, res, root, attachToRoot);
if (view != null) {
return view;
}
//运用xml进行解析回来一个XmlResourceParser,内部毕竟会进入native办法进行解析
XmlResourceParser parser = res.getLayoutapplication(resourc字体全国e);
try {
retandroid/yunosurn inflate(parser, root, attachToRoo字体规划t)字体规划;
} finally {
parser.close字体大全();
}
}

进入inflate中:

    //android.view.LayoutInflater
public View inflate(XmlPullParser parser, @Nullabl接口无权限e ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
...
try {
//找到START_TAG,不然报异常,说没有进口
advanceToRootNode(parser);
final String name = parser.getN接口卡ame();
...
//假设是merge标签,检查merge是不是根节字体点,不然的话抛出异常
if (TAG_MERGE.equals(name)) {
if (root =公积金= null || !attachToRappearanceoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and atta字体大全chToRoot=true");
}
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
//获取根节点View
final View temp =android下载 createViewFromTag(root, name, inflaterContext, att宫颈癌前期症状rs);
...
//烘托根节点的孩子们
rI字体转换器nAPPflateChildren(parser, temp, attrs, true);
...
return result;
}
}

进入rInflateChildren()工商银行客服电话中:

    final void rInflateChildren(Xandroid/yunosmlPull接口测验Parser parser, View parent, AttributeSet attrs,
boolean finishInflate) thrapplicationows XmlPullParserExandroid/yunosception, IOException {
rInflate(parse接口类型r, parent, papp下载arent.getContex宫颈癌t(), attrs, finishInflate);
}
void rInflate工商银行客服电话(XmlPullParser parser,宫颈癌 View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParseappointmentrException, IOException {
final int depth = parser.getDandroid体系epth();
int type;
boole工商银行客服电话an pendingRequestFocus = false;
//循环便利布局,直到布局完毕
while (((type = parser.nex接口类型t()) !工商银行= XmlPullParser.END_TAG ||
parser.getDepth() > depth) &&字体规划amp; type != XmlPullParser.END_DOCUMENT) {
...
//获取当时布局的View
fina接口类型l View view = createViewFromTag(parent, name, context, attrs);
fAPPinal ViewGroup viewGroupandroid是什么手机牌子 = (ViewGroup) pa接口rent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
//烘托当时布局View的孩子们
rInflateChildren(parser, view, attrs, true);
//组合为ViewGroup
vi接口是什么ewGroup.addView(view, params);
}
}
if (pendingRequestFocus) {
pare宫颈癌nt.restoreDefaultFocus();
}android的drawable类
if (finishInflate) {
p字体大小怎样调arent.o字体辨认扫一扫nFinishInflate();
}
}

进入createViewFromTag():

//android.view.LayoutInflater
View createViewFr龚俊omTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
// Apply a theme wrapper, if allowed and one字体辨认 is specified.
if (!ignoreThemeAttr) {
final TypedArrayappointment ta = context.obtainStyledAtappletributes(atandroid下载trs, ATTRS_THEME);
final int themeResId = ta.getResourceId(0, 0);
if (themeResId != 0) {
context = new ContextThemeWrapper(context, themeResId);
}
ta.recycle();
}
try {
View view = tryCreateView(parent, name, contAPPext, attrs);
if (view == null) {
final Object las龚俊tContext = mConstructorArgs[app是什么意思0];
mConstructorArgs[0] = context;
try {
if (-字体全国1 == name.i狗狗币ndexOf('.')) {
vie接口是什么w = onCreateView(contexGot, parent, name, attrs);
} else {
view = createView(context,宫崎骏 name, null, aandroid什么意思ttrs);
}
} finally {
mConstru字体辨认扫一扫ctorArgs[0] = lastContext;
}
}
return view;
} catch宫崎骏 (InflateException e) {
...
}
}

进行tryC字体全国reateView办法,假设viewGo为null,则实施onCreateView或许createView办法,

不管是onCreateView仍是createView毕竟都会进入如下函数:

    @Nullable
public final View createView(@NonNu工商银行客服电话ll Context viewContext, @No枸杞nandroid/yunosNull String name,
@Nul工商银行客服电话lable String prefix, @Nullablapp是什么意思e AttributeSet attrs)
throws Candroid的drawable类lassNotFoundException, I字体下载nflategoogleException {
Objects.req接口测验uireNonNull(viewContext);
Objects.requireNonNull(name);
Constructor<? extends View> constructor = sConstructorMap.get(name);
if (constructor != null && !verifyClassLoader(constructorAPP)) {
c字体管家onstructor = null;
sConstr字体uctorMap.remove(name);
}
Class<? extends View> clazz = null;
try {
Traceandroid的drawable类.traceBegin(Trace.TRACE_TGoAG_VIEW, name);
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it字体大小怎样调
clazz = Class.forName(prefix != nullandroid竞赛专用包 ? (prefix + name接口类型) : name, f字体下载alse,
mContext.getClassLoader()).as字体全国Subclass(View.class);
if (mFilter != null && clazz != null) {
boolean allowed = mFilter.onLoadClass(clazz);
if (!allowed) {
failNotAllowed(name, p接口测验面试题refix, viewCandroid的drawable类ontext, attrs);
}
}
constructor = clazz.getConsappstoretructor(mConstructorSignature);
construcappointmenttor.setAccessible(true);
sConstructorMagooglep.put(name, constructor);
} else {
// If we have a filter, apply it to cached constructor
if (mgoogleFilter != null) {
// Have we seen this name before?
Boolean allowedState = mFilterMap.get(nappointmentame);
if (allowedState == null) {
// New class -- remember whether it is allowed
clazz = Class接口.forName(preAPPfix != null ? (prefix + name) : name, false宫崎骏,
mContext.getClassLoader()).asSubclass(View.class);
boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
mFilterMap.put(name, allowed);
if (!allowed) {
failNotAllowed(name, prefix, viewContext, attrs);
}
} else if (allowedState.equals(Boolean.FALSE)) {
fail字体下载NotAllowed(name, prefix, vapproachiewContext, attrs);
}
}
}
Object lastContext = mConstructorArgs[0];
mConstructor接口和抽象类的差异Args[0] = viewContext;
Object[] args = mCon接口卡structorArgs;
args[1] = a字体大全ttrs;
try {
final View view = constrandroid手机uctor.newInstaandroid什么意思nce(args);
if (view instanceof ViewStub) {
// Use the sam宫颈癌e context when inflating ViewStub later.
final ViewStub viewStub = (ViewStub) view;
v狗狗币iewStub.s接口无权限etLayoutInfla狗狗币ter(cloneInContextandroid下载((Context) args[0]));
}
return view;
} finally {
mConstructorArgs[0] = lastContext;
}
} catch (NoSuchMethodException e) {
...
}
}字体大全

简单看一下可以确认是经过反射完毕的

然后咱们进入tryCreateView()办法:

//android.view.LayoutInflater
public final View tryCrea龚俊teView接口测验(@Nullable Vieappreciatew parent, @NonNull String name,
@NonNull Context context,
@NonNull AttributeSet attrs) {
if (name.equals(TAG_1995)) {
// Let's party like it's 1995!
return宫崎骏 new BlinkLayout(context, attrs);
}
View view;
if (mFactory2 != null) {
//体系一般会创立Factory2的政策
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) {
viewandroid什么意思 = mPrivateFactory.onCreateView(parent,字体大全 name, context, attrs);
}
return view;
}

那这个mFactory2/mFactory.onCreateView()要走哪里呢?Factory实例在哪里呢?

接下来

咱们需求剖析setCAPPontenView()之前googleandroid竞赛专用包做了什么

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

点击super.onCreate(saveGoInstanceState)办法:

    //androi接口文档dx.appcompat.app.AppCompatActivity工商银行客服电话
@OAPPverride
prapproachotected void onCreate(@Nullable Bundle savedInstanceState) {
final AppCompatDelegate delegate = getDelegate();
delegate.installViewFactory();
delegate.onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
}

进入instaandroid/yunosllViewFactory()办法:

    //androidx.appcompat.app枸杞.ApappearancepCompatDelegateImpl
@Override
pub字体美化大师lic void instal龚俊lViewFactory() {
Layou接口类型tInflater layoutInf字体全国la龚俊ter = LayoutInflatandroid竞赛专用包er.from(mContext);
if (layoutInflater.getFactory() == null) {
LayoutInflaterCompat.sandroid下载安装etFactory2(layoutInflater, this);
} else {
if接口和抽象类的差异 (!(layoutInflater.getFactory2() instanceof接口是什么 AppCompatDelegat字体规划eImpl)) {字体辨认
Log.i(TAG, "The Activity's LayoutInflater already hasapproach a Factory installed"接口卡
+ " so we can not接口无权限 install AppCompat's");
}
}
}

首要经过获取layout龚俊Inflater.getFactory()进行获取,假设没有就会setFactory字体美化大师2进行设置。

在其中进行setFactory初始化,留心第二个参数,传入的是this,那么,咱们看一下这个类:

class AppCompatDelegateImpl extends AppCompatDelegate
implements MenuBuilder.Callback, LayoutInflater.Factory2

很好,他是一个完毕Factory2接口的类,那么咱们直接找o接口crc过错计数nCreateView的完毕:

    @Override
public View createView(View parent, final String name, @android体系NonNull Context context,
@NonNull AttributeSet attrs) {
..接口无权限.
mAppCompatViewInflater = new AppCompatViewInflater();
...
return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,字体辨认扫一扫
I字体转换器S_PRE_LOLLIPOP, /* Only read android:theme pre-L (L+ handles this anyway) */
true, /* Read read ap字体美化大师p宫崎骏:theme as a fallback at all tiAndroidmes for legacy reasons */
VectorEnabledTintResources.shouldBeUsed() /* Only tint wrap the contex接口文档tappointment if enabled */
);
}

其实调用的是AppcompatVAPPiewInflater.createView()办法:

		final View createView(VGoiew parent, final String name, @NonNull Context context,
@NonNull Attrapp下载ibuteSet atandroid体系trs, boolean inheritContext,
boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
...
switch (nam宫颈癌前期症状e) {
casgooglee "TextAndroidView":
view =接口测验面试题 createTextView(context,宫颈癌前期症状 attrs);
verifyNotNull(view, name);
break;
...
default:
// The fallback thatapp下载 allows extending class to take over vi字体大小怎样调ew inflation
// foandroid手机r other tags. Note that we don't check that the result is not-null.
// T字体大全hat alandroid是什么手机牌子lows the custom inflater path to fall back on the default one
// later in this methoandroid下载安装d.
view = creat接口crc过错计数eView(context, name, attrs);
}
.接口类型..
return view;
}
@NonNull
protected AppCompatTextV接口卡iew createTextView(Context context, AttributeSet attrs) {
return new AppCompatTextView(context, attrs);
}

经过new的办法新建android平板电脑价格view,假设switch没有得到处理,则回来view为null,经过反射处理即可

咱们要怎样做?

Factory和Factory2的差异是什么?

迷惘咱们不能从文档说明上获得什么,咱们就直接看代码吧

        public interface Factory {
@Nullable
View onCandroid/yunosreateView(@NonN枸杞ull String namandroid体系e, @NonNull Context context,
@NonNull AappreciatettributeSet attrs);
}
public interface Facandroid下载安装tory2 extends Factory {
@Nullaandroid体系ble
View onCreateView(@Nullable View parent, @NonNull String name,
@NonNull Context context, @NonNull A龚俊ttributeSet attrs);
}

字段中多了一个View的父类,可以认为Factandroid体系ory2是对Factory的重载

在剖析进程中google,咱们发现

    //androidx.appcompat.aapp是什么意思pp.AppCompatDelegateImpl
@Override
public void inst接口测验allViewFactory() {
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
if (layout字体Inflater.android体系getFactory() == null) {
LayoutInflaterComp接口是什么at.setFactory2(layoutInflater, this);
} else {
if (!Go(layoutI字体管家nflater.getFactory2() instanceof AppCompatDelegateImpl)) {
Log.i(TAG, "The Activity's LayoutInflater alrappearanceeady handroid下载as a龚俊 Factory installed"
+ " so we can not install AppCompat's");
}
}
}

体系的处理是默许会取Factoryapp下载,假设没有字体转换器,才会新建,走AppCompatDelegateImpl,假设咱们在这个super.onCreate()办法之前设置Factory就可以让体系走咱们的Factory完毕

具体完毕

毕竟咱们在Activity的onCreate办法之前实施咱们的设置

    @Override
protected void onCreateappear(@Nullable Bundle savedInstanceState) {
LayoutInandroid平板电脑价格flaterCompat.setFactory(getLayoutInflater(), getSkinDelegate());
super.onCreate(savedInstanceStateapp下载);龚俊
}

一起在createV字体大全iew办法中生成自己接口crc过错计数的view,在view中设置改写接口,经过观察者方式在字体辨认扫一扫方式改动的时分穿心悉数的view,改动背景色,字体色等appearance,不需求从头构建Activity,防止闪屏