美文网首页
Android资源管理器过程分析 & 插件化实现Hook 资源管

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

作者: AntCoding | 来源:发表于2019-03-10 12:51 被阅读0次

    简 录

    概括[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插件化资源创建

    相关文章

      网友评论

          本文标题:Android资源管理器过程分析 & 插件化实现Hook 资源管

          本文链接:https://www.haomeiwen.com/subject/fyjkxqtx.html