简 录
概括[1]
资源分类
资源打包自定义资源ID的取值范围[2]
使用场景
自定义方式Android资源管理器AssetManager的创建过程[3]
上下文环境解析
多个APK环境下资源的存储方式
AssetsManger和Resources如何实现资源访问的Android-Framework-Plugin插件化资源创建[4]
概括
概括中主要讲解资源的基础认知及资源的打包过程和注意事项
资源分类
-
assets资源
-
res资源 > res目录下的资源会被赋予ID.方便我们通过ID访问资源
- animator -> 属性动画
- anim ->补间动画
- color ->颜色资源
- drawable -> 图片资源
- layout -> 布局资源
- menu ->菜单资源
- raw ->任意格式资源,它同assets下资源一样会原封不动的打入APK文件中,不过它仍会被赋予ID,我们也可以通过ID访问它
Resources res = getResources();
InputStream is = res .openRawResource(R.raw.filename);
- values ->描述一些简单资源
- xml ->描述应用程序的配置信息资源
注意:
- 上述9种类型资源,除了raw类型资源和Bitmap文件的drawable类型资源外,其余的资源都将在打包过程中编译成二进制格式的xml文件,
这些二进制文件分别有一个字符串资源池,用来保存文件中引用到的资源,
这样做的好处
- 文件占用更小
- 解析速度更快
- Res资源在编译完成后都会分配一个资源ID,会被保存到 R.java文件中与应用程序的其他源文件一起编译到程序中 ID唯一性,不唯一则出现资源冲突
资源打包过程
- 除了assets和res/raw资源被原封不动打进APK之外,其他资源会被编译成二进制文件打进APK
- 除了assets资源之外,其他资源都会被赋予一个资源ID
- 打包工具完成对资源的打包和编译后会生成一个 resources.arsc 文件和 R.java 文件,前者是 资源索引表,后者是 资源ID表
- AndroidManifest.xml同样会被编译成二进制的XML文件
- 应用程序通过AssetManager来访问资源,或通过资源ID访问,或通过文件名访问
自定义资源ID的取值范围
使用场景
当我们在插件定义的资源打包是编译到宿主资源链表时,为了避免这些资源与宿主的资源冲突,所以需要我们为宿主的资源定义一个ID范围.
自定以资源方式
在Android资源中,有一种资源类型称为Public,他们一般定义在res/values/public.xml文件中,形式如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--在宿主中指定这些id的范围是为了使宿主各类资源的id和插件各类资源的id不重复,否则会出错(若没出错也仅是凑巧)-->
<!-- 单独指定属性的资源范围是为了支持主题属性换肤 将end值设置为0100,意思预留从0000到0100的id给插件属性使用
即插件的attr不可超过256个,这个值可以根据实际情况调整
-->
<public-padding type="attr" name="public_static_final_host_attr_" start="0x7f010000" end="0x7f010100" />
<!-- 除属性外 其他类型的资源设置为 0x7f32开始 -->
<public-padding type="drawable" name="public_static_final_host_drawable_" start="0x7f320000" end="0x7f320000" />
<public-padding type="layout" name="public_static_final_host_layout_" start="0x7f330000" end="0x7f330000" />
<public-padding type="anim" name="public_static_final_host_anim_" start="0x7f340000" end="0x7f340000" />
<public-padding type="xml" name="public_static_final_host_xml_" start="0x7f350000" end="0x7f350000" />
<public-padding type="raw" name="public_static_final_host_raw_" start="0x7f360000" end="0x7f360000" />
<public-padding type="dimen" name="public_static_final_host_dimen_" start="0x7f370000" end="0x7f370000" />
<public-padding type="string" name="public_static_final_host_string_" start="0x7f380000" end="0x7f380000" />
<public-padding type="style" name="public_static_final_host_style_" start="0x7f390000" end="0x7f390000" />
<public-padding type="color" name="public_static_final_host_color_" start="0x7f3a0000" end="0x7f3a0000" />
<public-padding type="id" name="public_static_final_host_id_" start="0x7f3b0000" end="0x7f3b0000" />
<public-padding type="bool" name="public_static_final_host_bool_" start="0x7f3c0000" end="0x7f3c0000" />
<public-padding type="integer" name="public_static_final_host_int_" start="0x7f3d0000" end="0x7f3d0000" />
<public-padding type="array" name="public_static_final_host_array_" start="0x7f3e0000" end="0x7f3e0000" />
<public-padding type="menu" name="public_static_final_host_menu_" start="0x7f3f0000" end="0x7f3f0000" />
<public-padding type="mipmap" name="public_static_final_host_mipmap_" start="0x7f400000" end="0x7f400000" />
<public-padding type="animator" name="public_static_final_host_animator" start="0x7f410000" end="0x7f410000" />
<public-padding type="fraction" name="public_static_final_host_fraction" start="0x7f420000" end="0x7f420000" />
<public-padding type="font" name="public_static_final_host_font" start="0x7f430000" end="0x7f430000" />
<public-padding type="plurals" name="public_static_final_host_plurals" start="0x7f440000" end="0x7f440000" />
<public-padding type="interpolator" name="public_static_final_host_interpolator" start="0x7f450000" end="0x7f450000" />
<public-padding type="transition" name="public_static_final_host_transition" start="0x7f460000" end="0x7f460000" />
<public-padding type="_aapt" name="public_static_final_host__aapt" start="0x7f470000" end="0x7f470000" />
</resources>
xml分析
- type 用于指定资源类型
- name 用于appt在编译时重赋资源的name名称,name_资源名
- start 取值起始位置
- end 取值结束位置
每当aapt重新编译被修改过的资源时,都会重新给这些资源赋予ID
Android资源管理器AssetManager的创建过程
上下文环境解析
- 每个Activity都会关联一个ContextImpl对象,Activity和ContextImpl都继承于Context;
- 所以Activity大部分成员函数通过都调用ContextImpl对象的对应函数做处理的,其中访问程序资源是通过调用ContextImpl对象的getResources()和getAssets() 函数来实现的
- getResources()
返回一个Resources对象,通过Resources对象我们就可以通过资源ID访问资源
- getAssets()
返回一个AssetsManager对象,通过AssetsManager对象我们就可以通过文件名访问被编译过的资源文件
- 事实上Resources也是通过AssetsManager来访问资源文件的,只不过它会先通过资源ID查找到资源文件名
多个APK环境下资源的存储方式
Android系统允许一个进程中存在多个apk文件,每个APK文件都会有一个全局的Resources对象和AssetsManager对象,
- 全局的Resources对象存储在对应的ContextImpl对象的成员变量mResources中
- 全局的AssetsManager对象存储在对应的Resources对象的成员变量mAssets中
AssetsManger和Resources如何实现资源访问的
Resources访问资源
- Resources类中的成员函数getAssets(),通过getAssets()可以获取Resources类中成员变量mAssets的AssetsManager对象,所以访问资源最终实现仍在AssetsManager实现的
AssetsManager访问资源
- 1.AssetsManager类除了Java层的实现外,还有C++层对应的实现。而Java层的AssetsManager类的功能就是通过C++层来实现的;
- 2.Java层每个AssetsManager对象都有一个int类型的成员变量mObject,mObject保存的便是在C++层对应的AssetsManager对象地址.
- 3.通过mObject这个变量可以将Java层对象和C++层对象关联起来
- 4.C++层的AssetsManager类有三个重要的成员变量
- mAssetPaths -> 资源存放目录
- mResources -> 指向一个资源索引表,以供Resources对象通过资源ID访问资源
- mConfig -> 设备的本地配置信息,包括屏幕的密度和大小、国家地区、语言等
有了这三个变量后,C++层的AssetsManager类就可以访问程序资源了
Android-Framework-Plugin插件化资源创建
- 1.创建一个AssetManager的代理类的实例,因为是对AssetManager做代理,所以需要将AssetManasger的实例传入到代理中
HackAssetManager hackAssetManager = new HackAssetManager(assetMgr);
- 2.在HackAssetManager中通过反射将assetPaths数组中的资源路径添加进去
hackAssetManager.addAssetPaths(assetPaths);
- 3.完成以上步骤后我们需要创建字符串资源池来实现资源的添加
hackAssetManager.ensureStringBlocks(); //创建字符串资源池
4.现在我们可以获取资源了
Resources pluginRes = new PluginResourceWrapper(assetMgr, mainRes.getDisplayMetrics(),mainRes.getConfiguration(), pluginDescriptor);
// assetMgr 是我们融合后的Asset资源
//mainRes 是宿主的Resources实例,调用了其中的两个属性方法
//pluginDescriptor 是插件的属性数据序列化类实例
//通过实例化此得到一个Resources资源实例,
具体的实现代码请移步Android-Fragmework-Plugin,GitHub地址