Envoy Mobile Hello World 示例全解析:Java / Kotlin / Objective-C / Swift 四语言上手指南
2026/9/14 16:22:58 网站建设 项目流程

Envoy Mobile Hello World 示例全解析:Java / Kotlin / Objective-C / Swift 四语言上手指南

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

Envoy 不仅是服务端的高性能边车代理,它还被编译成移动端库(Envoy Mobile),直接嵌入 Android 与 iOS 应用,让 App 内的网络请求统一流经 Envoy 的 HTTP 引擎。本文围绕 mobile/docs/root/start/examples/hello_world.rst 这一官方入门文档展开:从四个 "hello world" 示例工程入手,逐步讲解如何在 Android 模拟器与 iOS 模拟器上构建、安装、运行示例,并深入源码剖析示例背后的EngineStreamClientPulseClient等核心 API,让读者既看得懂"怎么跑",也看得懂"为什么这么写"。

示例概览:1 秒定时器驱动的请求列表

hello_world.rst中描述的示例工程,其核心行为完全一致:启动 Envoy Mobile 引擎后,用一个 1 秒定时器周期性地发起网络请求,并把每次请求的响应(状态码与关键响应头)以列表形式展示在界面上。四个示例覆盖了移动端主流的四种开发语言:

  • Java:Android 平台,工程位于 mobile/examples/java/hello_world;
  • Kotlin:Android 平台,工程位于 mobile/examples/kotlin/hello_world;
  • Objective-C:iOS 平台,工程位于 mobile/examples/objective-c/hello_world;
  • Swift:iOS 平台,工程位于 mobile/examples/swift/hello_world。

从仓库目录结构看,四个示例工程同属 mobile/examples 下(另有 C++ 与 Python 的fetch_client示例),它们共享同一套响应展示逻辑:Android 侧复用了 mobile/examples/kotlin/shared 中的共享 UI 组件,iOS 侧则各自实现了简单的UITableView渲染。

前置准备:构建移动端产物

在运行任何示例之前,都需要先构建 Envoy Mobile 的发行产物。根据平台不同,分为两个目标:

  • Android 需要android_aar产物:它是一个 AAR(Android Archive),封装了 Envoy 的 native 引擎与 Java/Kotlin API,供android_binary依赖;
  • iOS 需要ios_framework产物:它是一个动态框架,封装了 Envoy 的 native 引擎与 Objective-C/Swift API,供 iOS App 链接。

在示例工程的 BUILD 文件中可以看到这一依赖关系。以 mobile/examples/java/hello_world/BUILD 为例,hello_envoy_java_lib直接依赖了//:envoy_mobile_android(即仓库根 BUILD 暴露的移动端 Android 产物),以及共享库//examples/kotlin/shared:hello_envoy_shared_lib。Kotlin 示例的 mobile/examples/kotlin/hello_world/BUILD 结构相同。

构建好对应产物后,再启动一个模拟器,即可进入各语言的运行步骤。

Java 示例:Android 上的 Hello Envoy

运行步骤

按官方文档的指引,Java 示例分三步走:

  1. 构建android_aar产物;
  2. 确保有一个正在运行的 Android 模拟器;
  3. 使用 Bazel 的mobile-install规则将示例 App 装入模拟器:
bazel mobile-install //examples/java/hello_world:hello_envoy --fat_apk_cpu=<arch1,arch2>

--fat_apk_cpu用于指定目标 CPU 架构,可传一个或多个(如arm64-v8a,x86_64),使 APK 同时包含多种 ABI 的 native 库。安装成功后,模拟器上会出现名为Hello Envoy的 App,打开即可看到请求源源不断地产生。

除了上述命令,仓库还提供了两个辅助脚本,在mobile目录下依次执行即可完成"起模拟器 + 装 App":

$ examples/java/hello_world/start_emulator.sh # 等待模拟器完全启动 $ examples/java/hello_world/start_app.sh

其中 start_emulator.sh 会通过sdkmanager安装 Android 30 (API 30) 的 Google APIs x86_64 系统镜像、用avdmanager创建名为test_android_emulator的 Pixel 4 模拟器并启动它;start_app.sh 则执行bazel build --config=mobile-android-release //examples/java/hello_world:hello_envoy,再通过adb install安装 APK,最后用adb shell am start直接拉起MainActivity。注意这两个脚本都要求设置ANDROID_HOME环境变量。

源码剖析:引擎、请求循环与统计上报

Java 示例的主逻辑位于 mobile/examples/java/hello_world/MainActivity.java。它演示了 Envoy Mobile 三个核心 API 的典型用法:

1. 构建引擎(Engine)AndroidEngineBuilder链式配置并build()出一个全局Engine实例,示例中开启了平台证书校验、将日志级别设为DEBUG、注册了日志回调与"引擎异步初始化完成"回调:

engine = new AndroidEngineBuilder(getApplication()) .setUseV2NetworkMonitor(true) .setUseQuicPlatformPacketWriter(true) .setLogLevel(LogLevel.DEBUG) .setLogger((level, message) -> { Log.d(TAG, message); return null; }) .setOnEngineRunning(() -> { Log.d(TAG, "Envoy async internal setup completed"); return null; }) .enablePlatformCertificatesValidation(true) .build();

2. 1 秒请求循环。代码启动了一个专用的HandlerThread,并postDelayed一个Runnable,在makeRequest()recordStats()之后再次以 1 秒为周期重排自身,形成持续请求:

handler.postDelayed(new Runnable() { public void run() { makeRequest(); recordStats(); handler.postDelayed(this, TimeUnit.SECONDS.toMillis(1)); } }, TimeUnit.SECONDS.toMillis(1));

makeRequest()通过engine.streamClient().newStreamPrototype()创建流,注册setOnResponseHeaderssetOnError回调后sendHeaders发起 GET 请求。示例请求的目标是api.lyft.com/ping,并且刻意在明文 HTTP 与 HTTPS 之间交替切换:服务器对 HTTP 请求返回 301、对 HTTPS 请求返回 200,示例据此判断成功与否,同时展示 Envoy 对 http/1.1(明文)与 h2(TLS)两条链路的处理能力——这一点在注释中明确说明是为了在 CI 端到端测试中同时覆盖两条路径。

3. 统计上报(Pulse)recordStats()演示了engine.pulseClient()的计数器用法:按foo.bar.counter三个Element维度定位一个计数器,先自增 1 再自增 5:

final Counter counter = engine.pulseClient().counter( new Element("foo"), new Element("bar"), new Element("counter")); counter.increment(1); counter.increment(5);

请求与统计结果通过ResponseRecyclerViewAdapter更新到RecyclerView列表。值得一提的细节是:Java 示例只注册了流回调,而 Kotlin/Swift 示例还额外注册了多个平台过滤器(platform filter)与 native 过滤器来演示 Envoy Mobile 的过滤器链能力,这正体现了四个示例"同骨架、各侧重"的设计。

Kotlin 示例:Android 上的过滤器链演示

运行步骤

Kotlin 示例的运行方式与 Java 完全一致,只是 Bazel 目标名不同:

bazel mobile-install //examples/kotlin/hello_world:hello_envoy_kt --fat_apk_cpu=<arch1,arch2>

安装成功后,模拟器上的 App 名为Hello Envoy Kotlin。同样,在mobile目录下可借助 mobile/examples/kotlin/hello_world/start_emulator.sh 与 mobile/examples/kotlin/hello_world/start_app.sh 完成一键起模拟器与安装。

源码剖析:平台过滤器与原生过滤器

Kotlin 示例的主逻辑位于 mobile/examples/kotlin/hello_world/MainActivity.kt。它的Engine配置比 Java 版丰富得多,集中展示了 Envoy Mobile 的过滤器链(filter chain):

engine = AndroidEngineBuilder(application) .setLogLevel(LogLevel.DEBUG) .setLogger { _, msg -> Log.d(TAG, msg) } .enableProxying(true) .addPlatformFilter(::DemoFilter) .addPlatformFilter(::BufferDemoFilter) .addPlatformFilter(::AsyncDemoFilter) .addNativeFilter( "envoy.filters.http.buffer", Any.newBuilder() .setTypeUrl("type.googleapis.com/envoy.extensions.filters.http.buffer.v3.Buffer") .setValue(ByteString.empty()) .build() .toByteArray() .toString(Charsets.UTF_8) ) .addStringAccessor("demo-accessor", { "PlatformString" }) .setOnEngineRunning { Log.d(TAG, "Envoy async internal setup completed") } .setEventTracker({ for (entry in it.entries) { Log.d(TAG, "Event emitted: ${entry.key}, ${entry.value}") } }) .build()

这里的三个平台过滤器(DemoFilterBufferDemoFilterAsyncDemoFilter)分别在工程目录下独立成文件,用于演示同步改写响应头、缓冲响应体、异步处理等典型场景;addNativeFilter则把服务端 Envoy 的 HTTP buffer 过滤器(envoy.filters.http.buffer)以原生方式挂进链中。请求流程与 Java 版一致:1 秒定时器驱动makeRequest()recordStats(),请求目标是api.lyft.com/ping(固定 HTTPS,因此上游走 h2),响应状态码为 200 即视为成功。相比 Java 版,Kotlin 版还会在日志中打印filter-demo响应头,直观展示过滤器链的效果。

Objective-C 示例:iOS 上的 Hello Envoy

运行步骤

iOS 侧不需要模拟器预启动,Bazel 会直接拉起模拟器并运行 App:

bazel run //examples/objective-c/hello_world:app --config=ios

命令执行后会自动启动一个 iOS 模拟器并打开新 App,随后就能看到请求开始流动。示例源码位于 mobile/examples/objective-c/hello_world。

源码剖析:Builder 配置与 NSTimer 循环

Objective-C 示例的主逻辑位于 mobile/examples/objective-c/hello_world/ViewController.m。它的EngineBuilder配置展示了 iOS 平台特有的能力,包括 DNS 缓存与键值存储:

EngineBuilder *builder = [[EngineBuilder alloc] init]; [builder setLogLevel:LogLevelDebug]; [builder enableDNSCache:YES saveInterval:1]; [builder addKeyValueStoreWithName:@"reserved.platform_store" keyValueStore:NSUserDefaults.standardUserDefaults]; [builder setOnEngineRunningWithClosure:^{ NSLog(@"Envoy async internal setup completed"); }]; id<Engine> engine = [builder build];

随后用NSTimer每 1 秒触发一次timerFired,其中依次调用performRequestrecordStats。请求通过RequestHeadersBuilder构造,经[engine streamClient]创建StreamPrototype、注册响应头/错误回调、startWithQueue:后在主队列上sendHeaders发出。示例请求目标是localhost:10000/ping(明文 HTTP,上游走 http/1.1),与 Swift 示例的 HTTPS/h2 形成对照——正如注释所言,这种差异是为了在 CI 端到端测试中同时验证两条路径。

统计部分通过[self.pulseClient counterWithElements:]创建计数器并incrementWithCount:累加。列表渲染上,成功条目以白底黑字展示消息与响应头,失败条目以红底白字展示错误信息。

Swift 示例:SwiftUI 时代前的经典 UITableView 版本

运行步骤

Swift 示例的运行命令与 Objective-C 完全相同,只是目标工程不同:

bazel run //examples/swift/hello_world:app --config=ios

执行后同样会启动模拟器并打开 App,请求随即开始流动。示例源码位于 mobile/examples/swift/hello_world。仓库中还包含一个基于 Swift Package Manager 的变体 mobile/examples/swift/swiftpm(含Package.swiftEnvoySwiftPMExample),适合不通过 Bazel 集成的工程参考。

源码剖析:Swift 版 Engine 配置

Swift 示例的主逻辑位于 mobile/examples/swift/hello_world/ViewController.swift。它的引擎配置在 Kotlin 版基础上换用了 Swift 语法,同样演示了平台过滤器、原生过滤器与事件追踪:

let engine = EngineBuilder() .setLogLevel(.debug) .addPlatformFilter(DemoFilter.init) .addPlatformFilter(BufferDemoFilter.init) .addPlatformFilter(AsyncDemoFilter.init) .respectSystemProxySettings(true) .addNativeFilter( name: "envoy.filters.http.buffer", typedConfig: "[type.googleapis.com/envoy.extensions.filters.http.buffer.v3.Buffer] { max_request_bytes: { value: 5242880 } }" ) .setOnEngineRunning { NSLog("Envoy async internal setup completed") } .addStringAccessor(name: "demo-accessor", accessor: { return "PlatformString" }) .setEventTracker { NSLog("Envoy event emitted: \($0)") } .build()

请求循环改用Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true),每次触发performRequest()recordStats()。请求目标是localhost:10000/ping(HTTPS,上游走 h2 over TLS),响应头过滤集合中额外包含async-filter-demo,与异步平台过滤器的演示相对应。sendHeaders(_:endStream:)发起请求后,成功/失败结果插入列表头部并刷新UITableView

四个示例的异同对照

维度JavaKotlinObjective-CSwift
平台AndroidAndroidiOSiOS
Bazel 目标//examples/java/hello_world:hello_envoy//examples/kotlin/hello_world:hello_envoy_kt//examples/objective-c/hello_world:app//examples/swift/hello_world:app
运行命令bazel mobile-install ... --fat_apk_cpu=<arch1,arch2>同左bazel run ... --config=ios同左
App 名称Hello EnvoyHello Envoy Kotlin
请求目标api.lyft.com/pingapi.lyft.com/pinglocalhost:10000/pinglocalhost:10000/ping
请求协议HTTP/HTTPS 交替(http/1.1 与 h2)HTTPS(h2)HTTP(http/1.1)HTTPS(h2 over TLS)
过滤器链演示无(仅流回调)3 个平台过滤器 + 1 个原生过滤器3 个平台过滤器 + 1 个原生过滤器
统计上报pulseClient计数器pulseClient计数器pulseClient计数器pulseClient计数器

从源码看,四个示例刻意保持了协议路径的互补:Java 与 Objective-C 走明文 HTTP(http/1.1),Kotlin 与 Swift 走 TLS(h2),这样 CI 的端到端测试可同时覆盖 Envoy Mobile 的两种主要上游协议路径。

从示例到生产:Envoy Mobile 的核心心智模型

透过四个 hello world 示例,可以提炼出 Envoy Mobile 的固定使用模式,这也是迁移到真实项目时的骨架:

  1. 引擎生命周期Engine是全局单例式的对象,通过 Builder 一次性配置(日志、证书校验、过滤器、回调),生命周期与 App 对齐;
  2. 流式请求 APIstreamClient().newStreamPrototype()返回可复用的流模板,注册setOnResponseHeaders/setOnError回调后startsendHeaders,天然支持非阻塞异步模型;
  3. 过滤器链:平台过滤器(addPlatformFilter,Kotlin/Swift 编写)与原生过滤器(addNativeFilter,复用服务端 Envoy 的 C++ 过滤器)可叠加组合,实现请求/响应改写;
  4. 统计与可观测性pulseClient提供基于Element维度的计数器等指标接口;setEventTracker可把引擎内部事件上报到自有埋点体系;
  5. 辅助工具:仓库中的start_emulator.sh/start_app.sh脚本(Java/Kotlin 工程内)封装了"创建模拟器 → 构建 → 安装 → 启动"的完整链路,可作为 CI 或本地开发脚本的参考模板。

总结

hello_world.rst篇幅虽短,但它是进入 Envoy Mobile 世界的第一个入口:四条命令、四个 App、四组最小可运行的代码,串起了引擎构建、定时请求、流式回调与统计上报这条完整的主线。配合 mobile/examples 下的真实源码逐行研读,即可快速建立起对 Envoy Mobile 编程模型的认识,为后续接入真实业务请求、自定义过滤器与埋点体系打下基础。

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询