SkinPlugin
参考
解决mFactorySet在Android Q中被非SDK接口限制的问题
效果图
思路
优点:
- 不闪烁
- 无需启动
- 架构独立
- 无继承
插件化 --- 现在
换肤:资源文件、图片、字体、文字颜色、等等
res
View view = tryCreateView(parent, name, context, attrs);
mFactory2 处理换肤
换肤的思路
-
知道 xml 的 View 怎么解析的?? ---》
-
如何拦截系统的创建流程? setFactory2 可以拦截 --- aop的思路去实现
-
拦截后怎么做?? 重写系统的创建过程的代码(复制)
-
收集 View 以及属性 每个 Activity 的 View 及属性都需要收集
-
创建皮肤包 -- apk
-
如何使用??只用插件的 res(插件的 java、res)
- 系统的资源是如何加载的? Resources、Assetmanager
- 通过Hook技术,创建一个 Assetmanager 专门加载皮肤包的资源
- 通过 反射 addAssetPath 方法放入皮肤包的路径 从而得到 加载皮肤包资源的 Assetmanager
- 首先通过 app 的资源 id --》 找到 app 的资源 name --》 皮肤包的资源 id
观察者模式、aop、Hook 技术
Hook 技术 --- 反射、动态代理的使用 通过反射、动态代理等技术 改变代码的原有流程
public void setFactory2(Factory2 factory) {
// Factory2 只能创建一次
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;
}
dispatchActivityCreated(savedInstanceState)@Activity.java
--> onActivityCreated
Resources AssertManager --> context
performLaunchActivity@ActivityThread.java
--> ContextImpl appContext = createBaseContextForActivity(r);
--> ContextImpl.createActivityContext
--> context.setResources
--> createResources
--> ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key);
--> impl = createResourcesImpl(key);
--> final AssetManager assets = createAssetManager(key);
--> builder.addApkAssets(loadApkAssets(key.mResDir, false /*sharedLib*/,
false /*overlay*/));
AssertManager 加载资源 --》 资源路径 --》 默认传入的资源路径 key.mResDir(app下面的res) 改成皮肤包的资源路径 --- Resources AssertManager 皮肤包的 Hook 的思路:不能改变原有的资源加载,单独创建一个 AssertManager--> 专门加载皮肤包的资源
皮肤包的
0x7f070092 t_window_bg res/drawable-hdpi-v4/t_window_bg.jpg
app的
0x7f070095 t_window_bg rgb8(0xffffffff)
skinResources.Drawable(0x7f070095);
首先通过 app 的资源 id --》 找到 app 的资源 name --》 皮肤包的资源 id
// app的resId
String resName=mAppResources.getResourceEntryName(resId); // 通过app的resId 找到 resName
String resType=mAppResources.getResourceTypeName(resId);// 通过app的resId 找到 类型,layout、drawable
// 获取对应皮肤包的资源Id
int skinId=mSkinResources.getIdentifier(resName,resType,mSkinPkgName);
参考:https://github.com/ximsfei/Android-skin-support#toc14
静态换肤 ---》 flag
if(){
tv.setColor(红色);
}else{
tv.setColor(黑色);
}