【Android】自定义换肤框架02之自定义AssetManager和Resource

server/2024/10/20 8:36:47/
ResourceId是如何变成对应Resource的

在上一章中,我们已经讲过,apk中有个资源索引文件

其中保存了每个资源对应的id,name,type,path

资源文件的解析,主要涉及两个类,AssetManager和Resource

  • AssetManager,用于管理apk中的原生资源文件,包括asset和resource
  • AssetManager通过调用addAssetPath方法,来添加提供资源的apk
  • addAssetPath默认使用的是context.packageResourcePath,及当前安装包的位置
  • 如果想加载其它apk里面的资源,就得自定义AssetManager
  • AssetManager的构造函数是因此功能,必须通过反射才能自己创建新的实例
  • Resource,用于管理resource文件夹下的资源,如color,drawable等
  • Resource解析资源前,首先要拿到apk中的资源索引文件,和屏幕信息,配置信息
  • Resource对象的构建依赖于AssetManager,DisplayMetrics,Configuration三个对象
  • 如果我们想从其它apk中加载资源,则需要提供自定义的AssetManager给Resource
  • 由于DisplayMetrics和Configuration信息是固定的,因此不需要自定义
设计思路
  • 当我们想根据皮肤去替换某个资源时,在skin.apk中创建一份同名,但内容不同的资源
  • 自定义SkinnerAssetManager,并绑定skin.apk
  • 自定义SkinnerResources,并绑定SkinnerAssetManager
  • 相同名称的资源,在不同apk中的id是不一样,但我们可以通过name+type+package的方式去找到对应的id
  • 通过OriginResourceId+OriginResources,得到name+type+package
  • 通过SkinnerResources,以及name+type+package,拿到SkinnerResourceId
  • 通过SkinnerResources+SkinnerResourceId,解析出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.Drawable@Suppress("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

且听下回分解!


http://www.ppmy.cn/server/55837.html

相关文章

【笔记】TimEP Safety Mechanisms方法论

1.TimEPM Overview 三大监控方法: Alive Supervision 实时监督Logical Supervision 逻辑监督Deadline Supervision 限时监督相关模块框图: 相关模块调用框图: 每个MCU核开启内狗(1核1狗),内狗用于监控相应核的TASK超时,超时后软reset MCU内狗时钟需要独立于OS时钟,两…

绝区贰--及时优化降低 LLM 成本和延迟

前言 大型语言模型 (LLM) 为各行各业带来了变革性功能,让用户能够利用尖端的自然语言处理技术处理各种应用。然而,这些强大的 AI 系统的便利性是有代价的 — 确实如此。随着 LLM 变得越来越普及,其计算成本和延迟可能会迅速增加,…

Qt 基础组件速学 鼠标和键盘事件

学习目标: 鼠标事件和键盘事件应用 前置环境 运行环境:qt creator 4.12 学习内容和效果演示: 1.鼠标事件 根据鼠标的坐标位置,做出对应的事件。 2.键盘事件 根据键盘的输入做出对应操作 详细主要代码 1.鼠标事件 #include "main…

基于Python爬虫的城市二手房数据分析可视化

基于Python爬虫的城市二手房数据分析可视化 一、前言二、数据采集(爬虫,附完整代码)三、数据可视化(附完整代码)3.1 房源面积-总价散点图3.2 各行政区均价3.3 均价最高的10个小区3.4 均价最高的10个地段3.5 户型分布3.6 词云图四、如何更换城市一、前言 二手房具有价格普…

007 HTTP

文章目录 HTTP协议概述什么是HTTP协议什么是URL/URI什么是WEB资源HTTP的作用是什么 HTTP协议版本HTTP协议组成请求协议信息请求行请求方法URI协议/版本 请求头请求体 响应协议信息状态行状态码说明 响应头响应体 GET请求和POST请求的区别提交数据的方式不同使用场景不同传递参数…

如何在ubuntu上安装ros-noetic?

如何在ubuntu上安装ros-noetic? 1. 源由2. 快速安装3. ROS学习 1. 源由 围绕ros-noetic这个系统,前面已经有不少谈及: Linux 35.5 JetPack v5.1.3ros-noetic安装Linux 35.5 JetPack v5.1.3Fast-Planner编译安装Linux 35.5 JetPack v5.1.…

【计算机网络】传输层(作业)

1、OSI参考模型中,提供端到端的透明数据传输服务、差错控制和流量控制的层是(C)。 A. 物理层B. 网络层C. 运输层D. 会话层 2、运输层为(B)之间提供逻辑通信。 A. 主机B. 进程C. 路由器D. 操作系统 3、运输层面向连接…

python中unittest框架和pytest框架区别

unittest框架和pytest框架对比 类型unittest框架pytest框架安装标准库,无需额外安装第三方单元测试库,需要安装用例编写测试类必须要继承 unittest.TestCase测试类命名以"Test"开头,无需继承,用例可以是函数&#xff0…