Android SDK · JNI 把 Java 接到 C++ 核心
Android 上的 WebRTC 是一层 JNI 桥:Java 的 PeerConnection 对象背后握着一个 native 指针,方法调用经 JNI 转到 C++ 核心。这一章读 android/ 的 native_api,讲清 JNI 桥的机制、scoped_java_ref/jvm 这些工具,以及跨语言时线程和引用管理的坑。
Android SDK · JNI 把 Java 接到 C++ 核心
上一章说各平台在 api/ 核心上加一层语言绑定。Android 这层用的是 JNI(Java Native Interface)——让 Java/Kotlin 代码调到底层 C++。这一章看它怎么搭。
Java 对象背后握着一个 native 指针
Android 开发者用的是 Java 类:PeerConnectionFactory、PeerConnection、VideoTrack……但它们只是壳:
每个 Java 侧的 WebRTC 对象(如 PeerConnection)内部握着一个 native 指针(long nativePeerConnection),指向 C++ 侧真正的 PeerConnectionInterface。Java 方法调用经 JNI 转成对那个 C++ 对象的调用。 于是:
- Java 侧
peerConnection.createOffer(...)→ JNI → C++ 侧PeerConnectionInterface::CreateOffer。 - C++ 侧的回调 / 事件(如
onIceCandidate)→ JNI 反向调用 → Java 侧的Observer。
Java 类是“遥控器”,真正干活的是 C++ 核心——协商、ICE、编解码、拥塞控制全在 native 层跑,和 iOS、桌面共享同一份代码。Java 侧只负责“把 Android 应用的调用转下去、把核心的事件转上来”。这就是上一章说的薄绑定:Android SDK 的绝大部分工作,是把 api/ 的 C++ 接口一对一映射成 Java 接口。
native_api:JNI 桥的工具箱
搭这座桥不简单——JNI 有一堆繁琐又易错的细节,WebRTC 在 android/native_api/jni/ 封了一套工具:
| 工具(源码) | 解决什么 |
|---|---|
jvm.h | 持有 JavaVM*,管理 JVM 生命周期,让 C++ 任意线程能找到 JVM |
scoped_java_ref.h | RAII 管理 JNI 引用(local/global ref),防止引用泄漏 |
class_loader.h | 缓存 Java 类的加载(JNI 查类很贵) |
java_types.h | Java ↔ C++ 类型转换(String、List、byte[] 的编组) |
application_context_provider.h | 拿到 Android 的 Context(很多平台能力要它) |
scoped_java_ref 值得单说——它是 scoped_refptr 思想在 JNI 上的翻版。 JNI 的引用分 local(方法返回自动失效)和 global(手动管理、忘了删就泄漏),裸用极易出错。ScopedJavaLocalRef / ScopedJavaGlobalRef 用 RAII 把“删引用”绑到析构上——出作用域自动清理,和 C++ 侧的引用计数哲学一脉相承。跨语言边界的资源管理,和核心里的对象生命周期管理是同一套 RAII 思路——这正是第 10 部分地基的思想在平台层的延续。
跨语言的两个经典坑:线程与引用
JNI 桥最容易出问题的两处,恰好呼应第 10 部分的主题:
- 线程附着 —— C++ 的任意线程要调 Java,必须先
AttachCurrentThread到 JVM(否则崩溃),用完 detach。WebRTC 的 worker/network 线程是 native 创建的,回调 Java 前都要处理附着。这是“native 线程模型”撞上“JVM 线程模型”的接缝。 - 引用生命周期 —— C++ 侧长期持有一个 Java 对象(如
Observer),必须用 global ref(local ref 出了 JNI 方法就失效),且用完要删——这又回到引用管理,scoped_java_ref就是为此。
Android SDK 的复杂度几乎全在这座桥上,而非 WebRTC 逻辑本身——逻辑都在共享的 C++ 核心里。理解这点,你调 Android WebRTC 的 bug 时就知道:多数“诡异问题”要么在 JNI 边界(线程 / 引用),要么在下面的核心(那就和其他平台一样,回到前十一部分)。
小结与下一章
Android 的 WebRTC 是一层 JNI 桥:每个 Java 对象(PeerConnection 等)握着 native 指针指向 C++ 侧的 api/ 对象,调用经 JNI 双向转发——Java 是遥控器,C++ 核心干活。android/native_api/jni/ 封了工具箱:jvm(JVM 生命周期)、scoped_java_ref(RAII 管 JNI 引用,scoped_refptr 的翻版)、java_types(类型编组)等。两个经典坑——线程附着(native 线程调 Java 前须 attach)和引用生命周期(长期持有须用 global ref)——都是第 10 部分主题在跨语言边界的再现。
Apple 平台的绑定是另一套风味。下一章看 iOS/macOS 的 Objective-C 封装。👉 iOS SDK 与 Objective-C。