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分析
  • 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插件化资源创建 ↩

你可能感兴趣的:(Android资源管理器过程分析 & 插件化实现Hook 资源管理器的实现)