bazel 移动安装

<ph type="x-smartling-placeholder"></ph> 报告问题 <ph type="x-smartling-placeholder"></ph> 查看来源 每晚 · 7.2 条 · 7.1敬上 · 7.0 · 6.5 · 6.4

针对 Android 的快速迭代开发

本页介绍 bazel mobile-install 如何进行迭代开发 快得多它介绍了此方法相对于 传统应用安装方式所面临的挑战。

摘要

如需非常快速地对 Android 应用进行细微更改,请执行以下操作:

  1. 找到要安装的应用的 android_binary 规则。
  2. 通过移除 proguard_specs 属性停用 Proguard。
  3. multidex 属性设置为 native
  4. dex_shards 属性设置为 10
  5. 通过 USB 连接运行 ART(而非 Dalvik)的设备并启用 USB 进行调试
  6. 运行 bazel mobile-install :your_target。应用启动速度会有点小 比平时慢。
  7. 修改代码或 Android 资源。
  8. 运行 bazel mobile-install --incremental :your_target
  9. 不必等待太久,

Bazel 的一些命令行选项可能很有用:

  • --adb 告知 Bazel 使用哪个 adb 二进制文件
  • --adb_arg 可用于向 adb 的命令行添加额外的参数。 一种实用的方法是选择 如果有多个设备连接到工作站: bazel mobile-install --adb_arg=-s --adb_arg=<SERIAL> :your_target
  • --start_app”会自动启动应用

如有疑问,请查看 示例与我们联系

简介

开发者工具链最重要的属性之一是速度: 更改代码与查看代码在一种环境下运行的 而且必须等待几分钟(有时是几个小时)才能收到任何反馈 判断您所做的更改是否符合您的预期

遗憾的是,用于构建 .apk 的传统 Android 工具链 必须执行所有这些连续步骤 构建 Android 应用在 Google,等待五分钟来构建单行 这在 Google 地图等大型项目中并不罕见

bazel mobile-install 通过 将更改剪枝、工作分片和对 Android 内部机制,无需更改任何应用代码。

与传统应用安装相关的问题

构建 Android 应用时会遇到一些问题,包括:

  • Dexing。默认情况下,在构建中仅调用一次,且 知道如何重复使用之前构建中的工作:它会再次对每个方法进行 dex 处理, 但只更改了一种方法。

  • 正在将数据上传到设备。adb 不使用 USB 2.0 的全带宽 大型应用可能需要很长时间才能上传完毕。整个应用 即使只有一小部分内容发生了更改,例如某个资源或 因此这可能会成为一个主要瓶颈。

  • 编译为原生代码。Android L 引入了 ART,一种新的 Android 运行时, 这种应用会预先编译应用,而不是像以前那样即时编译 Dalvik。这样可以显著提升应用运行速度,但代价是需要更长的安装时间 。这对用户来说是一个很好的权衡,因为用户通常会安装应用 并且多次使用,但是会导致应用的开发速度 多次安装,每个版本最多运行几次。

bazel mobile-install 的方法

bazel mobile-install 做出了以下改进:

  • dex 分片。在构建应用的 Java 代码后,Bazel 会将类分片 文件拆分为大小大致相同的部分,并在dx 。自上次构建以来没有更改的分片上不会调用 dx

  • 增量文件传输。Android 资源、.dex 文件和原生代码 这些库会从主 .apk 中删除,并存储在单独的 mobile-install 目录。这样就可以更新代码 而无需重新安装整个应用。因此, 传输文件所需的时间更短, 会在设备端重新编译。

  • 从 .apk 外部加载应用的某些部分。一个微小的桩应用 放入会加载 Android 资源、Java 代码和原生代码的 .apk 中 然后从设备端移动设备安装目录中复制控制权,然后将控制权转移给 实际应用除了在少数情况下,这对应用而言是透明的 如下所述。

分片 Dexing

分片 dexing 比较简单:构建 .jar 文件后, 工具 将它们分成大致相等大小的单独 .jar 文件,然后调用 dx。部署的逻辑 用于确定哪些 dex 碎片不是特定于 Android 的:它只使用 Bazel 的常规更改剪枝算法

第一版分片算法只是对 .class 文件进行排序。 然后将该列表分割成大小相等的部分,但事实证明, 不是最佳的:如果添加或移除了类(即使是嵌套或匿名类) 导致再次对这些分片执行 dex 处理。因此,决定将 Java 分片 而不是单个类。当然,这仍然会导致 对多个分片执行 dex 处理(如果添加或移除了新软件包),但这个数量要少得多 比添加或移除单个类更频繁。

分片数由 BUILD 文件(使用 android_binary.dex_shards 属性)。在理想情况下,Bazel 会 会自动确定最佳分片数量,但目前 Bazel 必须知道 一系列操作(例如,构建期间要执行的命令) 因此它无法确定最佳的分片数 因为它不知道最终会有多少 Java 类 应用。一般来说,分片越多,构建速度越快, 但应用启动速度会变慢,因为动态 就必须做更多工作最佳点通常介于 10 到 50 个分片之间。

增量文件传输

构建应用后,下一步是安装该应用,最好使用 尽可能省力安装包括以下步骤:

  1. 安装 .apk(通常使用 adb install
  2. 将 .dex 文件、Android 资源和原生库上传到 移动设备安装目录

第一步没有太大的增量:应用要么已经安装到 。Bazel 目前依赖于用户指示是否应执行此步骤 --incremental 命令行选项发送,因为它无法确定 所有情况。

在第二步中,系统会将 build 中的应用文件与设备端的应用文件进行比较 清单文件列出了设备上安装了哪些应用文件 校验和。系统会将所有新文件上传到此设备,以及所有已更改的文件 且所有已移除的文件都会从 设备。如果清单不存在,系统会假定每个文件都需要 上传。

请注意,您可以通过 更改设备上的文件,但不更改清单中的校验和。这可能会造成 通过计算 但我们认为不值得增加安装时间。

桩应用

存根应用程序是加载 dex 文件、原生代码和 设备端 mobile-install 目录中的 Android 资源发生。

实际加载是通过创建 BaseDexClassLoader 的子类实现的, 有据可查的分析法。这种情况发生在应用 这样,APK 中的任何应用类都可以 放置在设备上的 mobile-install 目录中,以便可以更新 不使用 adb install

必须先执行 类,因此无需在 .apk,这意味着对这些类进行更改将需要完整的 重新安装。

可通过替换在 Application AndroidManifest.xml 替换为 存根应用。这个 在应用启动时获得控制权,并调整类加载器和 合理使用资源管理器(其构造函数) 对 Android 框架内部机制的 Java 反射。

存根应用的另一项操作是复制原生库 通过移动设备安装功能安装到其他位置。这是必要的,因为 动态链接器需要对文件设置 X 位,但这 执行任何由非根 adb 可访问的位置的操作。

完成所有这些操作后,存根应用程序随后会将 实际的 Application 类,将所有对自身的引用更改为实际的 在 Android 框架中实现。

结果

性能

一般来说,bazel mobile-install 可使构建速度提高 4 到 10 倍 以及在稍作更改之后安装大型应用

以下数据是根据一些 Google 产品计算得出的:

当然,这取决于更改的性质:在 更改基本库需要更多时间。

限制

存根应用程序发挥的技巧并非在所有情况下都适用。 以下情况会突出显示它在哪些情况下无法按预期运行:

  • Context 转换为 Application 类时, ContentProvider#onCreate()。应用期间会调用此方法 然后才能替换 Application 的实例, 因此,ContentProvider 仍会引用桩应用 而不是真实的。当然,这不是错误 本来应该像这样将Context下推,但好像过几次才出现 Google 应用。

  • bazel mobile-install 安装的资源只能从 应用。如果其他应用通过 PackageManager#getApplicationResources(),这些资源将来自 上次非增量安装。

  • 未运行 ART 的设备。虽然桩应用适用于 在 Froyo 之后,Dalvik 有一个 bug,使它误以为该应用 如果代码分布在多个 .dex 文件上 例如,在 Java 注解用于 具体 。只要您的应用不会处理这些错误,就应该能使用 Dalvik, (但请注意,对旧版 Android 的支持并不是我们的 专注)