完全指南:从 SDK 组合到模拟器与 Ant 构建)
Nixpkgs Android 开发环境androidenv完全指南从 SDK 组合到模拟器与 Ant 构建【免费下载链接】nixpkgsNix Packages collection NixOS项目地址: https://gitcode.com/GitHub_Trending/ni/nixpkgs本篇技术指南围绕 nixpkgs 仓库中androidenv这一 Android 构建环境展开它提供了三大核心能力组合并部署定制化的 Android SDK含插件、系统镜像与 NDK、自动生成模拟器启动脚本emulateApp、以及基于 Ant 的 Android 应用构建buildApp。读完本文你将掌握composeAndroidPackages的全部参数语义、android-studio与androidPkgs的用法、环境变量与 Gradle 兼容性要点并能结合仓库源码理解latest、if-supported等机制在底层的真实实现。Android 构建环境的三大核心能力根据 doc/languages-frameworks/android.section.mdNix 提供的 Android 构建环境即androidenv围绕三个主要特性展开并附带一系列辅助能力SDK 组合composition声明式地指定平台版本、系统镜像、ABI、NDK、CMake 等插件一次性部署出完整的 SDK 目录模拟器编排emulator orchestration自动生成脚本按配置启动模拟器、等待开机完成、甚至安装并拉起指定 APK应用构建application build在纯 Nix 环境中构建基于 Ant 的 Android 工程产物是可直接安装的 APK。所有入口函数composeAndroidPackages、buildApp、emulateApp、androidPkgs都定义在 pkgs/development/mobile/androidenv/default.nix 中实现细节分布在同目录下的compose-android-packages.nix、build-app.nix、emulate-app.nix等文件中。与 Android Studio 结合使用 androidenv最直接的使用方式是利用android-studio-full属性获得一个非常完整的 Android SDK包含系统镜像{ buildInputs [ android-studio-full ]; }这与下面这条表达式完全等价{ buildInputs [ androidStudioPackages.stable.full ]; }另一种更灵活的方式是先把composeAndroidPackages组合出的 SDK 传给 Android Studio 的withSdkpassthrough{ buildInputs [ (android-studio.withSdk (androidenv.composeAndroidPackages { includeNDK true; }).androidsdk) ]; }无论采用哪种方式构建环境中都会自动导出两个关键环境变量ANDROID_HOME指向 SDK 目录ANDROID_NDK_ROOT指向 NDK 目录。这样 Android Studio、Gradle 乃至命令行工具就能在 Nix 环境中定位到正确的 SDK 位置。从源码看SDK 的实际路径是${androidsdk}/libexec/android-sdk见 compose-android-packages.nix 与 examples/shell.nix 中的ANDROID_HOME赋值。用 composeAndroidPackages 部署定制化 SDK当内置的完整 SDK 满足不了需求时可以单独部署一套 SDK并精确指定所需的插件集合或 SDK 子集。下面的表达式是文档中的完整示例with import nixpkgs { }; let androidComposition androidenv.composeAndroidPackages { platformVersions [ 34 35 latest ]; systemImageTypes [ google_apis_playstore ]; abiVersions [ armeabi-v7a arm64-v8a ]; includeNDK true; includeExtras [ extras;google;auto ]; }; in androidComposition.androidsdk该调用表示我们需要一个带有上述指定插件版本的 Android SDK。默认情况下绝大多数插件都是禁用的少数例外是tools、platform-tools与build-tools这几个基础子包这一点在源码 compose-android-packages.nix 的postInstall逻辑中体现——platform-tools、tools、build-tools是默认无条件链接的。顶层参数一览composeAndroidPackages支持的参数如下这些参数在源码compose-android-packages.nix的函数形参中均有对应定义与默认值参数作用默认值cmdLineToolsVersion指定cmdline-tools包版本latesttoolsVersion指定tools包版本。注意tools已过时目前只有26.1.1可选可设为null以排除latestplatformToolsVersion指定platform-tools插件版本latestbuildToolsVersions指定build-tools插件版本列表[ latest ]includeEmulator是否部署模拟器包可用emulatorVersion指定版本。设为if-supported时若系统支持则部署falseincludeCmake是否包含 CMake也支持if-supportedx86-64 与 Darwin 平台为true其余为falsecmakeVersions部署哪些 CMake 版本[ latest ]includeNDK是否包含 NDK 包可为true或if-supportedfalsendkVersions需要使用的 NDK 版本列表全部链接到 SDK 根目录下的ndk目录第一个额外链接到ndk-bundle目录latestndkVersion等价于在ndkVersions中只填一项若同时提供ndkVersions后者优先生效latestincludeExtras附加软件包标识符字符串数组例如extras;google;auto。注意 extras 可能不兼容所有平台例如 Google TV head unit 就没有 aarch64-linux 版本[ ]platformVersions需要包含的 SDK 平台版本默认只包含最新 API 级别可自行增加最新 API 级别numLatestPlatformVersions当使用默认platformVersions时包含多少个最新的 API 级别1可设为5以覆盖最近 5 年的 Android API 包minPlatformVersion/maxPlatformVersion若同时提供则优先于platformVersionsmaxPlatformVersion默认取最新版本因此可只设minPlatformVersion来表达所支持的最低 SDK 版本null/latest以上默认值均可在 compose-android-packages.nix 的函数签名中逐一核对。按平台版本生效的选项对于每个被指定的平台版本还可以应用以下选项includeSystemImages是否为每个平台 SDK 包含系统镜像includeSources是否为每个 SDK 版本包含源码useGoogleAPIs是否为每个选中的平台版本包含 Google APIuseGoogleTVAddOns是否为每个选中的平台版本包含 Google TV add-on。按系统镜像生效的选项对于每个请求的系统镜像还可以指定systemImageTypes要包含哪种系统镜像默认值为google_apis、google_apis_playstore、google_apis_ps16k与google_apis_playstore_ps16kabiVersions每种系统镜像要包含哪些 ABI 版本。需要说明的一点文档文字描述系统镜像 ABI 默认值为armeabi-v7a与arm64-v8a而当前仓库源码compose-android-packages.nix中的实际默认值是[ x86 x86_64 armeabi-v7a arm64-v8a ]四种 ABI——写表达式时建议显式指定abiVersions以源码实际行为为准。关于 latest 的解析语义大部分参数都有合理的默认值并且倾向于使用各工具的最新版本。对任何不关心具体版本的插件都可以直接写latest。从源码 compose-android-packages.nix 可以看到parseVersion会把字面量latest替换为repo.latest中对应 key 的真实版本parseVersions还会对版本列表做去重与排序parseVersion repo: key: version: if version latest then repo.latest.${key} else version;接受许可证extraLicenses还可以指定许可证名称列表extraLicenses是一组许可证名。可从repo.json或querypackages.sh licenses获取名称。只要把accept_license设为trueandroid-sdk-license就会自动被接受如果涉及预览版 SDK通常还需要加入android-sdk-preview-license或其它适用的许可证。底层实现中compose-android-packages.nix 会默认把android-sdk-license加入接受列表再合并extraLicenses当许可证未被接受时构建会直接throw并打印全部待接受许可证文本同时提示两种接受方式设置 nixpkgs 配置项android_sdk.accept_license true;单次调用时导出环境变量export NIXPKGS_ACCEPT_ANDROID_SDK_LICENSE1接受的许可证会以 sha1 哈希文件的形式写入 SDK 的licenses/目录。自定义仓库源repoJson 与 repoXmls可以覆盖composeAndroidPackages拉取包的仓库定义repoJson指向生成的repo.json文件路径。可以通过运行generate.sh其内部会调用mkrepo.rb来生成repoXmls一个包含仓库 XML 文件路径的属性集。一旦指定它优先于repoJson会触发一次本地构建、基于给定的仓库 XML 把repo.json写入 Nix store。注意这会用到 import-from-derivationIFD。{ repoXmls { packages [ ./xml/repository2-1.xml ]; images [ ./xml/android-sys-img2-1.xml ./xml/android-tv-sys-img2-1.xml ./xml/android-wear-sys-img2-1.xml ./xml/android-wear-cn-sys-img2-1.xml ./xml/google_apis-sys-img2-1.xml ./xml/google_apis_playstore-sys-img2-1.xml ]; addons [ ./xml/addon2-1.xml ]; }; }在源码中当repoXmls ! null时会用 update.rb 在构建期把 XML 集合转换成repo.json再通过lib.importJSON读取见 compose-android-packages.nix。构建上述表达式只需运行$ nix-buildAndroid SDK 就会被部署为带有全部所需插件版本的产物。部署 SDK 子集也可以只部署 Android SDK 的子集。例如只要platform-tools包with import nixpkgs { }; let androidComposition androidenv.composeAndroidPackages { # ... }; in androidComposition.platform-tools从源码看androidComposition这个属性集除了androidsdk之外还暴露了platform-tools、tools、build-tools、emulator、platforms、sources、system-images、cmake、ndk-bundle、google-apis、google-tv-addons等成员见 compose-android-packages.nix因此可以按需引用任意子集。使用预定义组合 androidPkgs除手动组合之外也可以直接使用一份预定义的、包含相当完整 Android 包的组合。部署整个 SDKwith import nixpkgs { }; androidenv.androidPkgs.androidsdk只使用某一个插件with import nixpkgs { }; androidenv.androidPkgs.platform-toolsandroidPkgs在 default.nix 中定义它实际就是对composeAndroidPackages的一次预置调用支持约最近 5 年的 Android 包与系统镜像numLatestPlatformVersions 5并将模拟器、系统镜像、NDK 均设为if-supportedandroidPkgs composeAndroidPackages { numLatestPlatformVersions 5; includeEmulator if-supported; includeSystemImages if-supported; includeNDK if-supported; };生成并启动模拟器实例出于测试目的自动生成带全部所需配置的模拟器启动脚本会非常方便。模拟器启动脚本由emulateApp {}函数生成with import nixpkgs { }; androidenv.emulateApp { name emulate-MyAndroidApp; platformVersion 28; abiVersion x86; # armeabi-v7a, mips, x86_64 systemImageType google_apis_playstore; }额外的模拟器 flag 可以通过运行时环境变量$NIX_ANDROID_EMULATOR_FLAGS传给 Android SDK 的 emulator。也可以指定一个 APK 部署到模拟器中并给出要启动的包名与 Activity 名with import nixpkgs { }; androidenv.emulateApp { name emulate-MyAndroidApp; platformVersion 24; abiVersion armeabi-v7a; # mips, x86, x86_64 systemImageType default; app ./MyApp.apk; package MyApp; activity MainActivity; }除了预构建的 APKapp参数还可以绑定到上一节buildApp {}函数的调用结果上即把 APK 参数指向一个 Nix 派生式。emulateApp 的更多参数与脚本行为对照 emulate-app.nix 的源码emulateApp还支持以下参数platformVersion默认35、abiVersion默认x86、systemImageType默认defaultenableGPU已弃用建议改用configOptions源码会在使用enableGPU时打印弃用警告并自动写入hw.gpu.enabled yesconfigOptions要追加到 AVDconfig.ini的键值对集合extraAVDFiles需要符号链接进 AVD 目录的额外文件列表deviceName默认deviceandroidUserHome/androidAvdHome旧变量名avdHomeDir仍被兼容控制虚拟设备存储位置避免污染用户的$HOMEsdkExtraArgs透传给composeAndroidPackages的额外参数androidAvdFlags/androidEmulatorFlags在NIX_ANDROID_AVD_FLAGS/NIX_ANDROID_EMULATOR_FLAGS为空时作为默认值注入。生成的脚本run-test-emulator会依次完成这些工作见 emulate-app.nix导出ANDROID_USER_HOME默认用mktemp -d建临时目录与ANDROID_HOME同时导出ANDROID_SDK_ROOT保持旧工具兼容在 55545584 的偶数端口范围内探测空闲 TCP 端口设置ANDROID_SERIALemulator-port端口耗尽时报错退出若 AVD 不存在用avdmanager create avd创建并按configOptions追加config.ini配置启动emulator -avd name -no-boot-anim -port port通过adb wait-for-device并轮询dev.bootcomplete属性等待设备完全开机若指定了app则用adb install安装 APK目录形式则取其中*.apk随后在提供package与activity时用am start拉起应用。Android 项目中的环境变量注意事项ANDROID_HOME应指向 Android SDK。在 Nix 表达式中应为${androidComposition.androidsdk}/libexec/android-sdk。注意ANDROID_SDK_ROOT已弃用但如果依赖它的工具需要也可以一并导出。ANDROID_NDK_ROOT在做 NDK 开发时应指向 NDK在 Nix 表达式中为${ANDROID_HOME}/ndk-bundle。使用 Android Gradle 插件时如果运行 Android Gradle 插件需要导出GRADLE_OPTS来把 aapt2 覆盖为 Nix store 中的 aapt2 二进制或者使用 FHS 环境让打包好的 aapt2 可以运行。如果不使用 FHS 环境类似下面的写法即可let buildToolsVersion 30.0.3; # Use buildToolsVersion when you define androidComposition androidComposition ...; in pkgs.mkShell rec { ANDROID_HOME ${androidComposition.androidsdk}/libexec/android-sdk; ANDROID_NDK_ROOT ${ANDROID_HOME}/ndk-bundle; # Use the same buildToolsVersion here GRADLE_OPTS -Dorg.gradle.project.android.aapt2FromMavenOverride${ANDROID_HOME}/build-tools/${buildToolsVersion}/aapt2; }使用 CMake 时如果使用 CMake需要在 shell hook 或 FHS env profile 中把 CMake 加入PATH。其路径以构建号结尾、但正确以版本号开头所以类似下面这样即可let cmakeVersion 3.10.2; # Use cmakeVersion when you define androidComposition androidComposition ...; in pkgs.mkShell rec { ANDROID_HOME ${androidComposition.androidsdk}/libexec/android-sdk; ANDROID_NDK_ROOT ${ANDROID_HOME}/ndk-bundle; # Use the same cmakeVersion here shellHook export PATH$(echo $ANDROID_HOME/cmake/${cmakeVersion}.*/bin):$PATH ; }仓库中的真实示例 examples/shell.nix 用通配符方式同时解决了这两个问题shellHook # Ensures that we dont have to use a FHS env by using the nix stores aapt2. export GRADLE_OPTS-Dorg.gradle.project.android.aapt2FromMavenOverride$(echo $ANDROID_HOME/build-tools/*/aapt2) # Add cmake to the path. cmake_root$(echo $ANDROID_HOME/cmake/*/) export PATH$cmake_root/bin:$PATH ... ;local.properties 与 Android Studio注意当ANDROID_HOME已设置时Android Studio 会自动写入一个local.properties其中sdk.dir指向$ANDROID_HOME若该文件尚不存在。如果同时使用 NDK可能还要在这个文件中添加ndk.dir。一个帮你把这些全部做完的shell.nix示例位于 examples/shell.nix。它的 shell hook 会以正确的sdk.dir与ndk.dir覆写local.properties从而保证在 nix-shell 内运行 Android Studio 时 SDK 与 NDK 目录都正确cat EOF local.properties # This file was automatically generated by nix-shell. sdk.dir$ANDROID_HOME ndk.dir$ANDROID_NDK_ROOT cmake.dir$cmake_root EOF提升 build.gradle 兼容性务必确保buildToolsVersion与ndkVersion和 androidenv 中声明的一致如果使用 CMake也要确保其声明的版本正确。否则可能遇到 aapt2 抛出的晦涩报错以及 Android Gradle 插件提示SDK 目录不可写、无法安装 build tools的问题android { buildToolsVersion 30.0.3 ndkVersion 22.0.7026061 externalNativeBuild { cmake { version 3.10.2 } } }这里的原则是Nix 构建出的 SDK 目录是只读的store 路径Gradle 无法在其中自行安装缺失的 build-tools因此两侧版本必须严格对齐。查询各插件的可用版本所有 androidenv 包都可以在 search.nixos.org 上检索到。需要说明的是当前aarch64-linux的兼容性还不太稳定而x86_64-linux与aarch64-darwin支持良好。原因是 Google 的仓库定义把某些仅面向x86_64或aarch64的包标记成了all架构。从源码compose-android-packages.nix可以看到Nix 系统标识会被映射为 Android 的 os/arch 标识x86_64-linux → linux/x64、aarch64-linux → linux/aarch64、aarch64-darwin → macosx/aarch64下载归档时再据此过滤archive.os与archive.arch。仓库还提供了querypackages.sh脚本querypackages.sh可以直接查询repo.json中的可用项./querypackages.sh packages ./querypackages.sh images ./querypackages.sh addons ./querypackages.sh extras ./querypackages.sh licenseslicenses模式会输出所有许可证名供extraLicenses使用其它模式会输出包路径: displayName形式的列表。更新生成的表达式repo.jsonrepo.json是根据 Android Studio 包管理器使用的 XML 文件生成的。要更新这些表达式可运行存放在pkgs/development/mobile/androidenv/子目录下的更新脚本./update.sh该更新会自动由 nixpkgs 的更新脚本触发。在当前仓库中与此对应的实际文件是 update.rb 与 fetchrepo.sh并且 README.md 给出了官方推荐的一键更新与测试命令$ nix-shell maintainers/scripts/update.nix --argstr package androidenv.test-suite --arg commit true $ export NIXPKGS_ALLOW_UNFREE1 $ nix-build -A androidenv.test-suitetest-suite派生式在 default.nix 中导出用于回归验证组合逻辑。用 Ant 构建 Android 应用buildApp除了提供 SDKandroidenv 还可以直接构建基于 Ant 的 Android 工程并自动部署项目所需的所有 Android 插件。大部分较新的 Android 项目使用 Gradle这部分主要是为历史项目保留with import nixpkgs { }; androidenv.buildApp { name MyAndroidApp; src ./myappsources; release true; # If release is set to true, you need to specify the following parameters keyStore ./keystore; keyAlias myfirstapp; keyStorePassword mykeystore; keyAliasPassword myfirstapp; # Any Android SDK parameters that install all the relevant plugins that a # build requires platformVersions [ 24 ]; # When we include the NDK, then ndk-build is invoked before Ant gets invoked includeNDK true; }除了应用专属的构建参数name、src、release以及 keystore 参数之外buildApp {}支持上一节 SDK 组合函数支持的全部函数参数——这一点在源码 build-app.nix 中体现为它通过builtins.intersectAttrs把composeAndroidPackages的正式参数从buildApp的入参中透传出去。从源码还可以看到更多实现细节release true时通过assert强制要求四个 keystore 参数全部非空并把它们写入ant.properties供签名使用build-app.nix构建过程会导出ANDROID_SDK_HOME指向当前目录避免密钥文件写入用户主目录若includeNDK true会先调用ndk-build再执行ant产物bin/*-release.apk或-debug.apk被移动到输出目录并额外写入nix-support/hydra-build-products文件。最后一个特性让buildApp在接入Hydra基于 Nix 的持续集成方案时格外有用Android APK 会作为构建产物暴露出来任何人在浏览器中访问构建结果页即可把 APK 安装到任意 Android 设备上。小结androidenv把 Android SDK 的下载、接受许可、解包、插件链接这一系列非确定性过程变成了纯 Nix 的声明式构建composeAndroidPackages负责精确组装 SDK 与插件androidPkgs提供开箱即用的预置组合emulateApp一键生成可复现的模拟器测试环境buildApp则打通了 Ant 工程的构建与 Hydra 集成。关键的经验法则可以总结为三条想让某参数取最新版本就写latest想让某组件在系统支持时自动启用就写if-supported务必让 build.gradle 中的 buildToolsVersion / ndkVersion / cmake 版本与 androidenv 声明保持一致。配合 examples/shell.nix 这样的现成模板即可在 nix-shell 中稳定复现一整套 Android 开发与测试环境。【免费下载链接】nixpkgsNix Packages collection NixOS项目地址: https://gitcode.com/GitHub_Trending/ni/nixpkgs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考