Android资源管理器过程分析 & 插件化实现Hook 资源管理器的实现

简 录

概括[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地址

This ALL! Thanks EveryBody!


  1. 概括

  2. 自定义资源ID的取值范围

  3. Android资源管理器AssetManager的创建过程

  4. Android-Framework-Plugin插件化资源创建

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容