Day945.Android系统开发的版本管理、编译与自动化测试 -系统重构实战

Android系统开发的版本管理、编译与自动化测试

Hi,我是阿昌,今天学习记录的是关于Android系统开发的版本管理、编译与自动化测试的内容。

从应用到系统开发,代码量从几十万行增长到几千万行,开发框架以及编译环境等与应用开发也不一样。所以如果要学习 Android 系统开发,需要先了解对应的开发框架及工具链。

一、Repo & Gerrit 代码管理

代码的管理。由于 Android 源码的代码量庞大,采用的是多个 Git 仓库来管理代码。

可以通过 GoogleSource 查看对应的仓库,大约有 3000 个仓库。

假如有一个需求开发涉及到跨多个仓库的修改,怎么来维护代码提交以及同步工作呢?

为了解决这个问题,官方提供了一个多 Git 仓代码管理的工具—— Repo

根据官网的介绍,Repo 不会取代 Git,目的是帮助在 Android 环境中更轻松地使用 Git。

Repo 将多个 Git 项目汇总到一个 Manifest 文件中,使用 Repo 命令来并发操作多个 Git 仓库的代码提交与代码同步很方便。


用表格梳理了一些 Repo 常用的命令:

在这里插入图片描述

通常使用 Repo 进行 Android 开发的基本工作流程如下图所示,包括创建分支、修改文件、提交暂存、提交更改以及最后上传到审核服务器。

在这里插入图片描述

另外,一般情况下,应用开发使用的代码审核工具都是类似于 GitLab 平台,通过临时分支拉取 Merge Request 提交代码审核,审核通过后,完成代码入库。针对于多仓库的代码审核,官方提供了另外一个代码审核工具—— Gerrit

Gerrit 是一个基于网页的代码审核系统,适用于使用 Git 的项目。Google 原先就是为了管理 Android 项目而设计了 Gerrit。

与 Merge Request 的代码审核差异是,Gerrit 采用 +1 +2 打分的方式来控制代码的合入,可以结合后面的截图来理解。

在这里插入图片描述

那么 Gerrit 是如何来关联多个仓库的代码提交记录呢?

这也是和 GitLab 等工具不同的地方,Gerrit 提供了标准的 “commit-msg” 钩子来生成 Change-Id。

通过 Change-Id,可以关联到多个代码仓库的提交,方便管理跨仓库的代码提交。


二、Soong 编译系统

前面从代码仓库管理可以看到,Android 系统有近 3000 多个仓库,如何来管理这么多代码的编译构建以及最终生成 img 镜像,自然是一个非常复杂的问题。为此,Google 在 Android 7.0 (Nougat) 中引入了 Sooong 编译系统,旨在取代 Make。

它利用 Kati GNU Make 克隆工具和 Ninja 构建系统组件来加速 Android 的构建。


一张示意图,梳理 Make、Kati、Soong、Ninja 等工具的关系。

在这里插入图片描述

Android.bp 与 Ninja 的区别在于, Android.bp 的目标对象是开发者,开发者基于 bp 的语法规则来编写脚本, Ninja 的目标是成为汇编程序,通过将编译任务并行组织,大大提高了构建速度。

从上图可以看出,Soong 通过 Android.bp 文件来定义和描述一个模块的构建。Android.bp 文件很简单,它们不包含任何条件语句,也不包含控制流语句。


以桌面的 Android.bp 文件为例,带你了解一下基本的 bp 语法规则,代码是后面这样。


//模块类型,定义构建产物的类型,例如这里的android_app就是定义生成APK类型
android_app {
    
    
    //应用名称
    name: "Launcher3",
    //编译所依赖的静态库
    static_libs: [
        "Launcher3CommonDepsLib",
    ],
    //编译源码路径
    srcs: [
        "src/**/*.java",
        "src/**/*.kt",
        "src_shortcuts_overrides/**/*.java",
        "src_shortcuts_overrides/**/*.kt",
        "src_ui_overrides/**/*.java",
        "src_ui_overrides/**/*.kt",
        "ext_tests/src/**/*.java",
        "ext_tests/src/**/*.kt",
    ],
    //编译资源路径
    resource_dirs: [
        "ext_tests/res",
    ],
    //配置混淆
    optimize: {
    
    
        proguard_flags_files: ["proguard.flags"],
        // Proguard is disable for testing. Derivarive prjects to keep proguard enabled
        enabled: false,
    },
    //配置编译相关的SDK版本号
    sdk_version: "current",
    min_sdk_version: min_launcher3_sdk_version,
    target_sdk_version: "current",
    //... ...
}

那么如何来触发执行编译呢?

Soong 支持整机编译以及指定模块编译。

先来看看如何完成整机的编译。

当下载完整个 AOSP 的源码后,进入到 AOSP 的根目录,输入后面的命令即可初始化编译环境。

source build/envsetup.sh

接下来,需要通过 lunch 命令选择要构建的目标,lunch 是 envsetup.sh 里定义的一个命令,用来让用户选择编译目标,如下图所示,选择对应的构建目标后,就可以通过 m 命令触发编译。

在这里插入图片描述

假如只要编译桌面这个 APP 怎么办呢?

前面提到 Soong 也支持编译单个模块,我们可以通过编译单个模块的命令触发编译,代码是后面这样。

// 进入桌面应用所在的目录
cd packages/apps/Launcher3
// 编译当前目录下的模块,不编译依赖模块
mm

另外,后面这 2 种方式也可以触发对一个模块的独立编译。

  • mma - 构建当前目录中的所有模块及其依赖项。
  • mmma - 构建提供的目录中的所有模块及其依赖项。

特别需要注意的是,Google 计划使用几年的时间将 Android 构建系统迁移到 Bazel。

Bazel 将取代 AOSP 中的所有现有构建系统和 build 配置系统(Make、Kati、Soong、基于 Make 的产品配置)。

对于这种明显的技术趋势,如果团队有条件,可以考虑提前做准备。


三、自动化测试

大部分的厂商都会基于 Android 系统扩展定制代码,为了保证厂商扩展代码后不会影响原来系统框架的功能,能够满足兼容性的要求,Google 提供了 CTS 以及 VTS 测试套件。

  • CTS(Compatibility Test Suite)中文为兼容性测试套件,主要用于测试 App 和 framework 的兼容性。

  • VTS(Vendor Test Suite)中文为供应商测试套件 ,主要会自动执行 HAL 和操作系统内核测试,如下图所示。

在这里插入图片描述

以 CTS 为例,最新的 Android 13 CTS 的测试模块大概约 1068 个模块,测试用例约 269 万个。

从这里可以看出,Google 对自动化测试的投入还是非常大的,也侧面反映了自动化测试的重要性。

CTS 的代码在 AOSP 源码的 cts 目录下,如果感兴趣,可以学习一下官方的测试设计与编写,CTS 中的测试主要也是使用 Instrumentation 以及 Junit Test,与前面介绍的应用测试编写类似。

在应用开发中使用 Gradle,可以通过 testDUT、testCAT 等命令来触发测试。


前面提到 Android 源码的编译系统采用的是 Soong,那么如果在 Android 系统中的一个模块添加测试,应该怎么来执行测试呢?

首先,可以定义测试模块的 android.bp 配置文件。

这里同样以桌面应用为例来看看,bp 配置文件代码是后面这样。

//配置文件模块为androidTest
android_test {
    
    
//测试模块名称
    name: "Launcher3Tests",
//测试目录
    srcs: [
        ":launcher-tests-src",
    ],
//依赖库
    static_libs: ["Launcher3TestLib"],
    libs: [
        "android.test.base",
        "android.test.runner",
        "android.test.mock",
    ],
//测试Launcher3模块
    instrumentation_for: "Launcher3",
    manifest: "AndroidManifest.xml",
    //... ...
}

可以编写相应的测试,这与应用的编写方式一致,下面看看桌面测试模块里面的一个简单的测试用例。

@SmallTest
@RunWith(AndroidJUnit4.class)
public class IntSetTest {
    
    
    @Test
    public void shouldBeEmptyInitially() {
    
    
        IntSet set = new IntSet();
        assertThat(set.size()).isEqualTo(0);
    }

    @Test
    public void oneElementSet() {
    
    
        IntSet set = new IntSet();
        set.add(2);
        assertThat(set.size()).isEqualTo(1);
        assertTrue(set.contains(2));
        assertFalse(set.contains(1));
    }
}

最后要解决的问题就是怎么运行这些测试用例了。

Google 官方提供了一个运行测试的工具 Atest,Atest 是一个命令行工具,可让用户在本地构建、安装并运行 Android 测试,同时可以大大加快重新运行测试的速度。

如果需要运行整个桌面测试模块的用例,可以直接执行如下命令。

atest Launcher3Tests

但如果只想运行模块内的单个类,可以使用 Module:Class 的方法,命令如下。

atest Launcher3Tests:IntSetTest 

更多关于 Android 系统开发的内容,如果感兴趣可以参考官网的 Android 开源项目,这个网站类似于应用开发的官方网站


四、总结

Android 系统开发的一些基础设施工具,与应用开发相比,系统开发更加复杂。

为了解决多仓库开发的问题,官方提供了 Repo 及 Gerrit 工具。

  • Repo 帮助我们可以去批量操作多个 Git 仓库,这大大简化了我们跨仓修改时代码提交同步的工作量。

  • Gerrit 工具也通过 Change-ID 的形式帮我们关联多个仓库的提交记录,方便我们做 CodeReview。

  • 另外,为了管理整机的编译以及独立模块的编译,Android 引入了 Soong 的编译系统,Soong 通过 Android.bp 文件来定义和描述一个模块的构建,最后转换为 Ninja 文件编译最终的目标产物。

  • 关于自动化测试,Google 官方设计 CTS 及 VTS 等兼容性套件,保证了框架的兼容稳定性。

  • 针对单个模块的自动化测试,也提供了 Atest 测试套件,帮助快速执行模块内的测试。

应用开发与系统开发使用的工具与方式总结成了一张表,供复习参考。

在这里插入图片描述


猜你喜欢

转载自blog.csdn.net/qq_43284469/article/details/130163747
今日推荐