资讯动态

使用 Mono 运行时在 Android 上测试 .NET 库:完整构建、执行与 CI 指南

发布时间:2026/9/20 14:04:44 来源:尧图企业网站定制
语言运行时标准库JIT编译编译器【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址https://gitcode.com/GitHub_Trending/runtime6/runtime点击查看免费下载本文基于 dotnet/runtime 仓库的 docs/workflow/testing/libraries/testing-android.md 编写系统讲解如何在 Android 平台模拟器/真机上借助 Mono 运行时构建并运行 .NET 库测试与功能测试。你将掌握环境依赖安装、monolibs与libs.tests的构建命令、AOT/AOT-LLVM/Interpreter 三种运行时配置的切换方式、测试 App 的底层运行机制Java Instrumentation JNI XHarness、日志采集方法以及 CI 流水线中升级 Android NDK 版本的三步流程。读完即可在本仓库环境中独立完成一次 Android 端 .NET 库测试的构建、部署与结果分析。[!NOTE] 本文面向的是Mono 运行时上的 Android 测试。若需在 Android 上使用 CoreCLR 运行时进行测试请参见 CoreCLR Android 文档。目录前置依赖为 Android 构建库与测试运行单个库的测试套件运行功能测试Functional Tests测试多种运行时配置AOT / AOT-LLVM / Interpreter测试 App 的设计与运行机制获取测试日志AVD Manager创建与启动模拟器现有局限性使用 Android Studio 调试原生运行时代码在 CI 流水线中升级 Android NDK 版本前置依赖要在本仓库中运行 Android 测试需要预先安装以下依赖OpenJDKJava 运行环境Android NDK原生开发工具包用于编译原生运行时与绑定代码Android SDK含 platform-tools、platforms、build-tools 等组件依赖的安装有两种方式纯终端命令行或使用 Android Studio 图形界面。使用终端安装在 LinuxUbuntu上OpenJDK 可以直接通过apt-get安装sudo apt-get install openjdk-8-jdk zip unzipAndroid SDK 与 NDK 可以通过官方仓库脚本自动下载安装。仓库文档给出了一个可直接复制的安装脚本要点如下#!/usr/bin/env bash set -e NDK_VERr27c ANDROID_CLI_TOOLS_VER13114758_latest SDK_API_LEVEL36 SDK_BUILD_TOOLS36.0.0 if [[ $OSTYPE darwin* ]]; then HOST_OSdarwin HOST_OS_SHORTmac BASHRC~/.zprofile else HOST_OSlinux HOST_OS_SHORTlinux BASHRC~/.bashrc fi # download Android NDK export ANDROID_NDK_ROOT~/android-ndk-${NDK_VER} curl https://dl.google.com/android/repository/android-ndk-${NDK_VER}-${HOST_OS}.zip -L --output ~/andk.zip unzip ~/andk.zip -d $(dirname ${ANDROID_NDK_ROOT}) rm -rf ~/andk.zip # download Android SDK, accept licenses and download additional packages such as # platform-tools, platforms and build-tools export ANDROID_SDK_ROOT~/android-sdk curl https://dl.google.com/android/repository/commandlinetools-${HOST_OS_SHORT}-${ANDROID_CLI_TOOLS_VER}.zip -L --output ~/asdk.zip mkdir ${ANDROID_SDK_ROOT} unzip ~/asdk.zip -d ${ANDROID_SDK_ROOT}/cmdline-tools rm -rf ~/asdk.zip yes | ${ANDROID_SDK_ROOT}/cmdline-tools/cmdline-tools/bin/sdkmanager --sdk_root${ANDROID_SDK_ROOT} --licenses ${ANDROID_SDK_ROOT}/cmdline-tools/cmdline-tools/bin/sdkmanager --sdk_root${ANDROID_SDK_ROOT} platform-tools platforms;android-${SDK_API_LEVEL} build-tools;${SDK_BUILD_TOOLS}该脚本的核心逻辑分四步下载 NDK从 Google 官方镜像下载对应宿主平台macOS 为darwin其余为linux的 NDK 压缩包并解压同时设置ANDROID_NDK_ROOT环境变量下载 SDK 命令行工具下载commandlinetools压缩包并解压到$ANDROID_SDK_ROOT/cmdline-tools下接受许可通过sdkmanager --licenses配合yes自动接受全部许可协议安装 SDK 组件依次安装platform-toolsadb 等、platforms;android-${SDK_API_LEVEL}平台 API与build-tools;${SDK_BUILD_TOOLS}构建工具。注意脚本中的版本号如NDK_VERr27c、SDK_API_LEVEL36、SDK_BUILD_TOOLS36.0.0是文档编写时的推荐值实际安装时应以你本地环境与仓库 eng/pipelines/extra-platforms/runtime-extra-platforms-androidemulator.yml 等 CI 配置中使用的版本为准。使用 Android Studio 安装Android Studio 提供了便捷的图形界面可以完成三件事一键安装上述全部依赖OpenJDK、SDK、NDK方便地创建和管理 Android 虚拟设备AVD在 IDE 内直接查看adb日志Logcat 窗口。为 Android 构建库与测试在开始构建之前建议先显式设置 Android SDK 与 NDK 的环境变量确保构建脚本能找到它们export ANDROID_SDK_ROOTPATH-TO-ANDROID-SDK export ANDROID_NDK_ROOTPATH-TO-ANDROID-NDK接下来就可以为 Android 构建全部内容了。首先构建 Mono 运行时与库./build.sh monolibs -os android -arch x64然后为每个库逐一构建并运行测试./build.sh libs.tests -os android -arch x64 -test运行前提确保模拟器已经启动参见下文 AVD Manager或者真机已通过 USB 连接、解锁屏幕。这里有一个容易踩坑的细节AVD Manager工具默认推荐安装x86镜像如果你遵循了这一推荐那么构建脚本中的-arch参数必须使用x86而不是x64否则构建出的二进制与模拟器架构不匹配测试将无法运行。运行单个库的测试套件如果不希望跑全量测试可以针对某个具体库单独运行测试。下面以System.Numerics.Vectors为例./dotnet.sh build /t:Test src/libraries/System.Numerics.Vectors/tests /p:TargetOSandroid /p:TargetArchitecturex64 /p:RuntimeFlavormono这条命令通过仓库根目录的dotnet.sh引导脚本调用 MSBuild核心参数含义参数作用/t:Test指定执行 MSBuild 的Test目标负责构建并运行测试/p:TargetOSandroid目标操作系统为 Android/p:TargetArchitecturex64目标架构为 x64若模拟器为 x86 镜像则改为x86/p:RuntimeFlavormono使用 Mono 运行时风味注意这里通过/p:TargetOSandroid显式传入TargetOS与./build.sh中的-os android是等效的 MSBuild 属性写法。运行功能测试Functional Tests仓库中存在专门针对目标移动平台上特定特性、配置与运行模式进行验证的功能测试位于 src/tests/FunctionalTests 目录下Android 相关子目录见 src/tests/FunctionalTests/Android/Device_Emulator。功能测试的运行方式与普通库测试完全一致例如运行 PInvoke 功能测试./dotnet.sh build /t:Test -c Release /p:TargetOSandroid /p:TargetArchitecturex64 /p:RuntimeFlavormono src/tests/FunctionalTests/Android/Device_Emulator/PInvoke/Android.Device_Emulator.PInvoke.Test.csproj成功返回码约定当前功能测试约定以返回42作为成功标志因此在新增功能测试时请务必注意这一点。这一点在仓库源码中有直接佐证。以 PInvoke 功能测试为例Program.cs 通过DllImport(__Internal)调用原生函数并注册托管回调最终Main返回counter值为42[DllImport(__Internal)] unsafe private static extern void invoke_external_native_api(delegate* unmanagedvoid callback); [UnmanagedCallersOnly] private static void Callback() { counter 42; } public static int Main() { unsafe { delegate* unmanagedvoid unmanagedPtr Callback; invoke_external_native_api(unmanagedPtr); } return counter; }同时其项目文件 Android.Device_Emulator.PInvoke.Test.csproj 中通过ExpectedExitCode42/ExpectedExitCode显式声明了期望退出码MonoForceInterpretertrue/MonoForceInterpreter RunAOTCompilationfalse/RunAOTCompilation TestRuntimetrue/TestRuntime MainLibraryFileNameAndroid.Device_Emulator.PInvoke.Test.dll/MainLibraryFileName IncludesTestRunnerfalse/IncludesTestRunner ExpectedExitCode42/ExpectedExitCode而 eng/testing/tests.mobile.targets 会把该属性透传给 XHarnessAdditionalXHarnessArguments Condition$(ExpectedExitCode) ! $(AdditionalXHarnessArguments) --expected-exit-code $(ExpectedExitCode)/AdditionalXHarnessArguments即 XHarness 会以--expected-exit-code 42校验测试结果从而形成功能测试返回 42 即成功的闭环约定。测试多种运行时配置AOT / AOT-LLVM / Interpreter通过组合若干额外的 MSBuild 属性如RunAOTCompilation、MonoForceInterpreter等可以针对同一条测试命令测试多种运行时配置1. AOT 模式仅使用 AOT预先编译全静态编译模式构建/p:RunAOTCompilationtrue /p:MonoForceInterpreterfalse2. AOT-LLVM 模式在 AOT 基础上启用 LLVM 后端获得更高性能的原生代码/p:RunAOTCompilationtrue /p:MonoForceInterpreterfalse /p:MonoEnableLLVMtrue3. Interpreter解释器模式强制使用 Mono 解释器执行/p:RunAOTCompilationfalse /p:MonoForceInterpretertrue这些属性最终会被 Android 应用构建任务消费。仓库中的 AndroidAppBuilder.cs 定义了对应任务属性例如ForceAOT模拟器优先 FullAOT 而非 JIT、ForceFullAOT是否 AOT 全部程序集、ForceInterpreter是否强制解释器模式、RuntimeFlavor默认为Mono等/// summary /// Prefer FullAOT mode for Emulator over JIT /// /summary public bool ForceAOT { get; set; } /// summary /// Indicates if we want to AOT all assemblies or not /// /summary public bool ForceFullAOT { get; set; } public bool ForceInterpreter { get; set; } public string RuntimeFlavor { get; set; } nameof(RuntimeFlavorEnum.Mono);由此可见RunAOTCompilation/MonoForceInterpreter/MonoEnableLLVM这类文档层面的配置开关最终会映射到 AndroidAppBuilder 任务内部控制 Mono 运行时的编译与执行策略从而决定 APK 中托管程序集的呈现形态原生对象文件、中间语言或混合形态。测试 App 的设计与运行机制理解测试 App的内部构造有助于排查问题。Android 测试应用本质上由两部分组成一个 Java Instrumentation即仓库中的 MonoRunner.java 模板配合一个简单的 Activity通过JNI初始化 Mono 运行时一个 xunit 测试执行器Mono 运行时启动后会运行名为XHarness.TestRunner的简单 xunit runner来自 dotnet/xharness 工具链执行 bundle 中所有*.Tests.dll的测试。此外还有XHarness.CLI工具它内嵌了 ADB可以将*.apk部署到目标设备真机或模拟器并在测试完成后取回日志与结果。从 MonoRunner.java 源码可以看到整个生命周期静态初始化块通过loadLibrary触发各原生库的JNI_OnLoadonCreate解析adb传入的 instrumentation 参数env:前缀用于设置环境变量entrypoint:libname用于指定入口程序集initializeRuntime把 assets 中的assets.zip解压到应用私有目录unzipAssets设置HOME、TMPDIR、TEST_RESULTS_DIR等环境变量再通过 JNI 调用initRuntime初始化 MonoonStart调用executeEntryPoint执行入口程序集将返回码写入result的return-code并在存在testResults.xml时记录test-results-path最后finish(retcode, result)结束 instrumentation测试结果默认写到getExternalFilesDir(DIRECTORY_DOCUMENTS)但在 Android API 30 上因adb pull权限问题见 dotnet/xharness issue #385会回退到getCacheDir()。这与前文功能测试返回 42 即成功的约定完全对应入口程序集Main的返回值经由 JNI 一路传递最终成为 instrumentation 的退出码供 XHarness.CLI 校验。获取测试日志XHarness 在 Android 上默认话不多——它只把测试结果保存到文件即上文的testResults.xml与return-code。如果需要实时查看运行日志可以订阅 live logadb logcat -s DOTNET或者更简单的方式直接打开 Android Studio 或 Visual Studio 中的logcat窗口。DOTNET标签贯穿整个测试链路MonoRunner.java中所有关键节点都会打印该标签的日志例如MonoRunner initializeRuntime, entryPointLibName...、MonoRunner finished, return-code...、MonoRunner finished, test-results-path...因此按标签过滤可以快速定位 .NET 侧的状态信息。AVD Manager创建与启动模拟器测试需要目标设备。如果安装了 Android Studio可以直接在 IDE 中使用AVD Manager创建并启动 Android 虚拟设备。否则Android SDK 自带的avdmanager命令行工具也能完成同样工作。下面是一个从命令行安装系统镜像、创建并启动模拟器的完整示例SDK_API_LEVEL需与已安装的 Android SDK 平台版本一致EMULATOR_NAME_X86/EMULATOR_NAME_X64为自定义名称# Install x86 image ${ANDROID_SDK_ROOT}/cmdline-tools/tools/bin/sdkmanager system-images;android-${SDK_API_LEVEL};default;x86 # Create x86 image ${ANDROID_SDK_ROOT}/cmdline-tools/tools/bin/avdmanager create avd --name ${EMULATOR_NAME_X86} --package system-images;android-${SDK_API_LEVEL};default;x86 # Launch emulator with x86 image ${ANDROID_SDK_ROOT}/emulator/emulator -avd ${EMULATOR_NAME_X86} # Install x64 image ${ANDROID_SDK_ROOT}/cmdline-tools/tools/bin/sdkmanager system-images;android-${SDK_API_LEVEL};default;x86_64 # Create x64 image ${ANDROID_SDK_ROOT}/cmdline-tools/tools/bin/avdmanager create avd --name ${EMULATOR_NAME_X64} --package system-images;android-${SDK_API_LEVEL};default;x86_64 # Launch emulator with x64 image ${ANDROID_SDK_ROOT}/emulator/emulator -avd ${EMULATOR_NAME_X64} 模拟器支持非常多的启动选项完整列表可通过emulator -help查看。再次提醒模拟器镜像架构x86 / x86_64必须与构建参数-arch x86 / x64保持一致。现有局限性在当前版本中Android 端测试存在以下已知限制规划测试方案时需提前考虑Windows 上暂不支持-os androidWindows 宿主无法直接进行 Android 构建可改用WSLWindows Subsystem for Linux规避XHarness.CLI 尚不能自动启动模拟器必须通过AVD Manager或 IDE 手动启动模拟器再执行测试AOT 与 Interpreter 模式暂未支持指部分功能测试场景下仍受限需结合具体测试套件判断。使用 Android Studio 调试原生运行时代码如果测试暴露出 Mono 原生运行时C/C 层的问题可以使用 Android Studio 附加调试器。详细的调试指南参见仓库文档 Debugging Android它涵盖了原生代码的符号配置、调试器附加与断点设置等完整流程。在 CI 流水线中升级 Android NDK 版本Android NDK 有两条发布通道滚动发布rolling release大约每季度一次长期支持LTS release每年一次通常在 Q3。发布日期并不保证但 LTS 版本至少获得一年的支持或持续到下一个 LTS 进入 RC候选发布阶段为止此后该 NDK 版本将不再获得 bug 修复与安全更新。升级时机与版本策略LTS NDK 的发布时间表大致与 .NET RC 时间线对齐因此建议在main分支中该时间点前后规划 NDK 升级。若能在 .NET 正式发布前完成升级可以确保 CI 构建与测试在发布后的约 9 个月内运行在受支持的 NDK 版本上。同时需要考虑 .NET MAUI 的支持周期每个 .NET 发布后 MAUI 支持 18 个月。这意味着 CI 中使用的 NDK 版本仅覆盖单个 .NET MAUI 发布生命周期的大约一半。如果希望 CI 使用的 NDK 在某个 .NET MAUI 发布的整个生命周期内都受支持则应在release 分支中也考虑升级 NDK 版本。CI 中 NDK 版本的来源CI 流水线使用的 NDK 版本来自dotnet-buildtools-prereqs-docker仓库托管的 Docker 镜像。例如 Azure Linux 3.0 .NET 10.0 Android Dockerfile 即属于此类镜像定义本仓库内可参考 eng/pipelines/extra-platforms/runtime-extra-platforms-androidemulator.yml 等流水线对 Android 镜像与环境的引用方式。在 prereqs 仓库中提升 NDK 版本会自动传播到所有 CI 运行因此为保证 CI 持续正常运作NDK 升级需要遵循三步流程1. 本地验证新 NDK 版本下载新 NDK 版本使用新 NDK 构建一个示例 Android 应用验证本地构建是否通过确保构建中启用了AOT与AOT_WITH_LIBRARY_FILES。2. 在 CI 中测试新 NDK 并修复问题基于 dotnet-buildtools-prereqs-docker 仓库中的原始 Docker 镜像创建一个包含新 NDK 版本的 Docker 镜像在runtime仓库中打开一个draft PR草稿 PR将 Dockerfile 引用更新为新镜像观察 CI 结果并修复所有失败CI 全绿后仅提交必要的改动如修复、构建调整到相应分支不要在最终提交中改动 Docker 镜像引用该引用后续会由 prereqs 仓库的版本自动流动替换。3. 在 prereqs 仓库更新 NDK 版本修改 dotnet-buildtools-prereqs-docker 仓库中的 Dockerfile更新 NDK 版本合并后更新后的 NDK 将自动流向给定分支的所有构建。通过上述三步可以在保持稳定性与兼容性的前提下平滑地完成 CI 中 Android NDK 的升级。总结在 Android 上借助 Mono 运行时测试 .NET 库是一条构建 → 部署 → 执行 → 取日志的完整链路环境上需要 OpenJDK Android SDK Android NDK且模拟器架构与-arch参数必须一致构建上通过./build.sh monolibs -os android -arch x64与./build.sh libs.tests -os android -arch x64 -test完成全量构建与测试也可用./dotnet.sh build /t:Test ...精确到单个库或单个功能测试项目运行模式上通过RunAOTCompilation/MonoForceInterpreter/MonoEnableLLVM三组属性在 AOT、AOT-LLVM、Interpreter 之间切换机制上测试 App 由 Java InstrumentationMonoRunner.java通过 JNI 初始化 Mono再交由 XHarness.TestRunner 执行*.Tests.dll最终以返回码功能测试约定为42与testResults.xml汇总结果XHarness.CLI 借助内嵌 ADB 完成 APK 部署与结果回收维护上NDK 升级需遵循本地验证 → CI 草稿 PR 验证并修复 → prereqs 仓库更新版本的三步流程保证 CI 的稳定性与 NDK 支持窗口。赞分享语言运行时标准库JIT编译编译器【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址https://gitcode.com/GitHub_Trending/runtime6/runtime点击查看免费下载相关推荐在 .NET 运行时仓库中构建、运行与调试 Android 上的 CoreCLR完整开发者工作流在 .NET 运行时仓库中构建、运行与调试 Android 上的 CoreCLR完整开发者工作流 导读 本文基于 .NET 运行时仓库dotnet/runt语言运行时标准库JIT编译编译器在 .NET MAUI Android 应用中使用 BenchmarkDotNet 运行基准测试完整实战指南在 .NET MAUI Android 应用中使用 BenchmarkDotNet 运行基准测试完整实战指南 导读 本文基于 .NET MAUI 官方仓库前端移动开发桌面应用跨平台UI组件如何轻松配置Windows和Office面向新手的终极解决方案指南如何轻松配置Windows和Office面向新手的终极解决方案指南 还在为Windows系统频繁弹出配置提示而烦恼吗Office突然变成只读模式无法保存文件语言运行时标准库JIT编译编译器上一篇如何为Qwen3-4B配置MindIE容器从镜像加载到容器创建的完整指南下一篇初学者必看ner-bertje-tagdetekst API参数详解与实战案例 创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价