做钢材的网站有哪些,网站开发作用,上海建设机械网站,wordpress 评论者邮箱ResourceId是如何变成对应Resource的
在上一章中#xff0c;我们已经讲过#xff0c;apk中有个资源索引文件
其中保存了每个资源对应的id#xff0c;name#xff0c;type#xff0c;path
资源文件的解析#xff0c;主要涉及两个类#xff0c;AssetManager和Resource …ResourceId是如何变成对应Resource的
在上一章中我们已经讲过apk中有个资源索引文件
其中保存了每个资源对应的idnametypepath
资源文件的解析主要涉及两个类AssetManager和Resource
AssetManager用于管理apk中的原生资源文件包括asset和resourceAssetManager通过调用addAssetPath方法来添加提供资源的apkaddAssetPath默认使用的是context.packageResourcePath及当前安装包的位置如果想加载其它apk里面的资源就得自定义AssetManagerAssetManager的构造函数是因此功能必须通过反射才能自己创建新的实例Resource用于管理resource文件夹下的资源如colordrawable等Resource解析资源前首先要拿到apk中的资源索引文件和屏幕信息配置信息Resource对象的构建依赖于AssetManagerDisplayMetricsConfiguration三个对象如果我们想从其它apk中加载资源则需要提供自定义的AssetManager给Resource由于DisplayMetrics和Configuration信息是固定的因此不需要自定义
设计思路
当我们想根据皮肤去替换某个资源时在skin.apk中创建一份同名但内容不同的资源自定义SkinnerAssetManager并绑定skin.apk自定义SkinnerResources并绑定SkinnerAssetManager相同名称的资源在不同apk中的id是不一样但我们可以通过nametypepackage的方式去找到对应的id通过OriginResourceIdOriginResources得到nametypepackage通过SkinnerResources以及nametypepackage拿到SkinnerResourceId通过SkinnerResourcesSkinnerResourceId解析出skin.apk中的color或drawable由于并不是所有属性都会跟随皮肤而变换因此SkinnerResourceId有可能不存在如果SkinnerResourceId不存在则使用OriginResources去加载原来的资源这样大致实现了资源的自动加载
自定义SkinnerAssetManager
package com.android.library.skinnerimport android.app.Application
import android.content.res.AssetManager
import android.content.res.Resources
import android.graphics.drawable.DrawableSuppress(Deprecated)
object SkinnerAssetManager {lateinit var context: Applicationlateinit var assetManager: AssetManagerlateinit var skinnerResources: Resourceslateinit var originResources: Resourcesfun init(application: Application, resourcePath: String) apply {context applicationcreateHookedAssetManager(resourcePath)}private fun createHookedAssetManager(resourcePath: String) {val assetManager AssetManager::class.java.newInstance()val method AssetManager::class.java.getDeclaredMethod(addAssetPath, String::class.java)method.invoke(assetManager, resourcePath)this.originResources context.resourcesval resources Resources(assetManager, originResources.displayMetrics, originResources.configuration)this.assetManager assetManagerthis.skinnerResources resources}fun skinResId(resId: Int): Int {return skinnerResources.getIdentifier(originResources.getResourceName(resId),originResources.getResourceTypeName(resId),originResources.getResourcePackageName(resId))}fun skinColor(resId: Int): Int {val skinResId skinResId(resId)if (skinResId 0) {return skinnerResources.getColor(skinResId)}return originResources.getColor(resId)}fun skinDrawable(resId: Int): Drawable {val skinResId skinResId(resId)if (skinResId 0) {return skinnerResources.getDrawable(skinResId)}return originResources.getDrawable(resId)}
}拷贝测试皮肤包到存储卡
这里我们将测试包放在asset文件夹里面在应用启动时拷贝到存储卡从而省去人工操作
private fun copySkinPackage() {val fis application.assets.open(skin.apk)val fos FileOutputStream(sdcard/skin.apk)val buffer ByteArray(fis.available())fis.read(buffer)fos.write(buffer)
}通过指定皮肤包初始化SkinnerAssetManager
SkinnerAssetManager.init(application, sdcard/skin.apk)使用自定义的SkinnerAssetManager加载资源
val drawable SkinnerAssetManager.skinDrawable(R.drawable.icon_app)
binding.image.setImageDrawable(drawable)十万个为什么
到目前为止我们已经实现了从指定apk中加载同名资源
下一步问题是如何让Activity/Fragment/View/Xml使用SkinnerResources而不是默认的OriginResources
且听下回分解