实现Android插件化的核心技术简介(二):Android dynamic load resources

实现Android插件化的核心技术(二):Android dynamic load resources

Android resources are loaded by AssetManager.

安卓资源由AssetManager加载。

While android application launched, it creates an AssetManager instance and add resources search paths by addAssetPath.

By default, it adds:

应用启动时,系统会为其创建一个AssetManager实例,并由addAssetPath方法添加资源搜索路径,默认添加:

  1. "/framework/base.apk" - Android base resources (base)
  2. "/data/app/*.apk" - The launching apk resources (host)

As we know, android resources are accessed by an unique index with format 0xPPTTNNNN.

所有的资源需要通过一个唯一的id来访问

  • PP - the resource package id (00-02: system, 7f: application, 03-7e: reserved)
  • TT - the resource type id (types are 'attr', 'layout', 'string' and etc.)
  • NNNN - the resource entry id

To avoid id conflicts, the resources ids in host and plugins should be partitioned.

为避免id冲突,宿主以及各个插件之间的资源id需要分段处理。

There are some ways to do this:

  1. Create a self-contained asset manager for each plugin. 插件独立使用一个资源管理器

    • ❌Plugins can not access resource to each other. 插件间无法访问资源
  2. Add all plugins' asset path to host's asset manager. 添加所有资源路径到宿主

    1. Arrange the entry ids (NNNN) by public.xml 分配NN段

      • ⚠️ High cost of maintenance. 字段有限,不好维护
    2. Arrange the type ids (TT) by public.xml 分配TT段

      • ❌Plugins can not access resource to each other. 插件间无法访问资源
    3. Arrange the package ids (PP) 分配PP段

      1. Modify aapt source code 修改aapt源码

        • ⚠️ High cost of maintenance. 不好维护
      2. Modify the products of aapt (binary resources.arsc and *.xml) 修改aapt生成产物

        • ✅Seamless, and the ability of ultimate slicing. 无缝连接,支持极致剪裁

Now, out final plan is to repack android asset package and reset package id of it's resources.

我们的最终方案是对资源包进行重新打包,重设资源id

Repack android asset package

First of all, unzip the package file resources.ap_, get files:

首先,解压resources.ap_文件,可以得到:

AndroidManifest.xml
resources.arsc
res
  `-- layout
       `-- activity_main.xml

They are all binary files, data structs are in ResourcesType.h

这些文件都是二进制的,数据结构参见ResourcesType.h

Repack resources.arsc
chunktypenote
Table headerRES_TABLE_TYPE = 0x002 
Resource stringsRES_STRING_POOL_TYPE = 0x001e.g. 'Hello World!'
Package headerRES_TABLE_PACKAGE_TYPE = 0x200Rewrite entry: package id
Type stringsRES_STRING_POOL_TYPE = 0x001e.g. 'attr', 'layout', etc.
Key stringsRES_STRING_POOL_TYPE = 0x001e.g. 'activity_main', etc.
DynamicRefTableRES_TABLE_LIBRARY_TYPE = 0x0203Insert entry for Android 5.0+
Type specRES_TABLE_TYPE_SPEC_TYPE = 0x0202 
Type infoRES_TABLE_TYPE_TYPE = 0x0201Rewrite entry: resource entry value
  1. Reset package id

  2. Filter types

  3. Dynamic package reference

    In Android 5.0+ needs to set the dynamicRefTable for lookup bag parent.

    5.0以上需要对二进制文件加入“资源包id映射”数据段,以使得能正确查找到主题等bag的parent。

    What's the bag and bag parent?

    For example, we made a plugin with package id 0x34, and defined themes in it's styles.xml:

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">..</style>
    <style name="AppTheme.NoActionBar">..</style>

    And then after aapt executed, we got a bag tree as following: (run aapt d --values resources *.apk to see more)

    Theme.AppCompat.Light.DarkActionBar
        `-- AppTheme <bag> (id: 0x34050001)
              `-- AppTheme.NoActionBar <bag> (id: 0x34050000)
    

    The AppTheme.NoActionBar's parent is AppTheme, but in ResourcesType.cpp, If has not pre-define dynamicRefTable (declare what 0x34 is), while looking up the parent ofAppTheme.NoActionBar (0x34050000) it would abort and never find AppTheme andTheme.AppCompat.Light.DarkActionBar.

    The result is that, if your activity is an instance of AppCompatActivity but cannot apply theAppCompat Theme, then a crash raised. Let's see the codes:

    ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
            uint32_t* outTypeSpecFlags) const
    {
        ...
            bag_entry* cur = entries+curEntry;
    
            cur->stringBlock = entry.package->header->index;
            cur->map.name.ident = newName;
            cur->map.value.copyFrom_dtoh(map->value);
            status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value);
            if (err != NO_ERROR) {
                ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data);
                return UNKNOWN_ERROR;
            }
        ...
    }
    status_t DynamicRefTable::lookupResourceValue(Res_value* value) const {
        ...
        status_t err = lookupResourceId(&value->data);
        if (err != NO_ERROR) {
            return err;
        }
        ...
    }
    status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
        uint32_t res = *resId;
        size_t packageId = Res_GETPACKAGE(res) + 1;
    
        if (packageId == APP_PACKAGE_ID) {
            // No lookup needs to be done, app package IDs are absolute.
            return NO_ERROR;
        }
    
        if (packageId == 0) {
            // The package ID is 0x00. That means that a shared library is accessing
            // its own local resource, so we fix up the resource with the calling
            // package ID.
            *resId |= ((uint32_t) mAssignedPackageId) << 24;
            return NO_ERROR;
        }
    
        // Do a proper lookup.
        uint8_t translatedId = mLookupTable[packageId];
        if (translatedId == 0) {
            //--------------------------------------------------------
            // Lookup Code Block
            // [0x03, 0x7e] goes here
            //--------------------------------------------------------
            ALOGV("DynamicRefTable(0x%02x): No mapping for build-time package ID 0x%02x.",
                    (uint8_t)mAssignedPackageId, (uint8_t)packageId);
            for (size_t i = 0; i < 256; i++) {
                if (mLookupTable[i] != 0) {
                    ALOGV("e[0x%02x] -> 0x%02x", (uint8_t)i, mLookupTable[i]);
                }
            }
            return UNKNOWN_ERROR;
        }
    
        *resId = (res & 0x00ffffff) | (((uint32_t) translatedId) << 24);
        return NO_ERROR;
    }
Repack *.xml
chunktypenote
Xml headerRES_XML_TYPE = 0x0003 
Attr idsRES_XML_RESOURCE_MAP_TYPE = 0x180Rewrite entry: each attr id
Xml nodeRES_XML_START_ELEMENT_TYPE = 0x102Rewrite entry: node attribute value

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android核心技术与实例详解 图书简介: 本书以Android应用程序的开发为主题 并结合真实的案例向读者详细介绍了Android的基本组件的使用及应用程序开发的整个流程 全书分为三篇共18章 第一篇以简单易懂的实例为依托 详细介绍了Android平台的基本控件 高级控件 常用界面布局及菜单对话框等相关知识;第篇介绍了Android平台的高级知识 包括消息与广播服务 网络数据处理 手机通信功能 桌面组件 多媒体采集 Google服务及3D程序开发等;第三篇则对三个真实案例的开发步骤进行详细介绍 逐步向读者讲解Android手机应用程序的真实开发过程 同时在源代码中还包含了详细的注释 以尽量帮助读者掌握代码中的每一个细节 尽快掌握Android编程 本书的讲述由浅入深 从介绍Android平台的基本组件到带领读者开发大型应用程序 结构清晰 语言简洁 非常适合初学者和进阶开发者阅读参考 本书附赠DVD光盘1张 其中包含了大容量的手把手教学视频 电子教案 PPT 实例源代码等 Android核心技术与实例详解 图书目录: 基础篇 第1章 Android开发起步 1 1 Android平台简介 1 1 1 Android背景介绍 1 1 2 Android平台架构 1 2 Android开发环境的搭建 1 2 1 相关软件的下载与安装 1 2 2 虚拟设备的创建和使用 1 2 3 创建“Hello Android” 1 2 4 Android应用程序的调试 1 3 小结 第2章 Android应用程序的构成 2 1 Android应用程序的解析 2 1 1 目录结构 2 1 2 资源的管理与使用 2 1 3 AndroidManifest xml简介 2 1 4 应用程序的权限 2 2 Android基本组件的介绍 2 2 1 应用程序的生命周期 2 2 2 Activity简介 2 2 3 Service简介 2 2 4 BroadcastReceiver简介 2 2 5 ContentProvider简介 2 2 6 Intent和IntentFilter简介 2 3 小结 第3章 Android布局管理器 3 1 控件类概述 3 1 1 View类简介 3 1 2 ViewGroup类简介 3 2 线性布局 3 2 1 LinearLayout类简介 3 2 2 线性布局案例 3 3 表格布局 3 3 1 TableLayout类简介 3 3 2 表格布局案例 3 4 相对布局 3 4 1 RelativeLayout类简介 3 4 2 相对布局案例 3 5 帧布局 3 5 1 FrameLayout类简介 3 5 2 帧布局案例 3 6 绝对布局 3 6 1 AbsoluteLayout类简介 3 6 2 绝对布局案例 3 7 小结 第4章 Android常用基本控件 4 1 文本控件的介绍 4 1 1 TextView类简介 4 1 2 EditText类简介 4 1 3 文本框使用案例 4 2 按钮控件 4 2 1 Button类简介 4 2 2 ImageButton类简介 4 2 3 9Patch图片简介 4 2 3 9Patch图片使用案例 4 3 状态开关按钮 4 3 1 ToggleButton类简介 4 3 2 开关按钮的使用 4 4 单选按钮与复选按钮 4 4 1 CheckBox和RadioButton类简介 4 4 2 单选按钮和复选按钮使用案例 4 5 图片控件 4 5 1 ImageView类简介 4 5 2 图片查看器 4 6 时钟控件 4 6 1 AnalogClock和DigitalClock类简介 4 6 2 时钟控件使用案例 4 7 日期与时间选择控件 4 7 1 DatePicker类简介 4 7 2 TimePicker类简介 4 7 3 日期时间控件使用案例 4 8 动画播放技术 4 8 1 帧动画简介 4 8 2 帧动画使用案例 4 8 3 补间动画简介 4 8 4 补间动画使用案例 4 9 小结 第5章 Android常用高级控件 第6章 菜单与对话框 高 级 篇 第7章 Android事件处理模型 第8章 游戏与3D应用程序开发 第9章 消息广播与服务 第10章 网络与数据处理 第11章 手机通信功能开发 第12章 手机特有Feature开发 第13章 桌面组件与多媒体数据采集 第14章 传感器应用的开发 第15章 Google服务 第16章 Android游戏开发实践 快乐数独 案例篇 第17章 Android地图搜索应用 美食天下 第18章 Android社交分享平台 口袋微博">Android核心技术与实例详解 图书简介: 本书以Android应用程序的开发为主题 并结合真实的案例向读者详细介绍了Android的基本组件的使用及应用程序开发的整个流程 全书分为三篇共18章 第一篇以简单易懂的实例为依托 详细 [更多]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值