杰瑞科技汇

C与Java如何协同开发Android应用?

在当前的 Android 开发生态中:

C与Java如何协同开发Android应用?-图1
(图片来源网络,侵删)
  1. Java 仍然是主流和官方推荐的应用层开发语言,绝大多数 Android App 的 UI、业务逻辑、网络请求等都是用 Java (或其近亲 Kotlin) 编写的。
  2. C/C++ 主要用于“性能优化”和“硬件交互”,当 Java 代码在处理某些任务(如游戏物理引擎、视频/音频编解码、图像处理、科学计算)感到吃力时,或者需要直接操作硬件(如通过蓝牙、USB 与外设通信)时,就会引入 C/C++。
  3. 它们通过 Java Native Interface (JNI) 机制桥接,这是 Java 代码调用 C/C++ 代码,以及 C/C++ 回调 Java 代码的唯一官方标准方式。

Java 在 Android 开发中的角色

Java 是 Android 平台的“一等公民”,也是过去十多年来的绝对主力。

Java 的主要用途:

  • 应用层开发:编写 App 的所有可见和不可见逻辑。
    • UI 布局与交互:通过 XML 布局文件和 Java/Kotlin 代码创建用户界面,响应用户点击、滑动等操作。
    • 业务逻辑:实现 App 的核心功能,比如用户登录、数据展示、订单处理等。
    • 网络通信:使用 HttpURLConnection, OkHttp, Retrofit 等库进行网络请求和数据解析。
    • 数据存储:使用 SharedPreferences, SQLite, Room 等进行本地数据持久化。
    • 依赖注入与架构:使用 Dagger, Hilt, MVVM, MVP 等现代架构模式组织代码。

Java 的优势:

  • 跨平台:“一次编写,到处运行”,Java 代码在 Android 设备上通过 Dalvik/ART 虚拟机执行,无需为不同硬件重新编译。
  • 开发效率高:拥有极其丰富的第三方库(生态系统成熟),开发工具链(Android Studio, Gradle)非常完善。
  • 内存管理自动:拥有自动垃圾回收机制,开发者无需手动管理内存,大大减少了内存泄漏和悬垂指针的风险。
  • 类型安全:静态类型语言,在编译阶段就能发现很多错误。

Java 的劣势(也是引入 C/C++ 的原因):

  • 性能开销:运行在虚拟机上,执行效率低于原生代码。
  • 无法直接操作硬件:出于安全考虑,Java 代码无法直接访问内存地址或硬件寄存器。
  • 在某些计算密集型任务上表现不佳:例如复杂的 3D 渲染、实时音视频处理等。

C/C++ 在 Android 开发中的角色

C/C++ 并不用于编写整个 App,而是作为“性能增强模块”或“硬件抽象层”被引入。

C/C++ 的主要用途:

  • 游戏开发
    • 游戏引擎:像 Unity 和 Unreal Engine 的核心就是用 C++ 编写的,它们负责渲染、物理模拟等。
    • 物理引擎:计算碰撞、力、运动等,需要极高的性能。
  • 音视频处理
    • 编解码:对视频进行 H.264/H.265 的编码和解码,对音频进行 AAC/MP3 的编解码,这些算法复杂,计算量大,用 C/C++ 实现效率极高。
  • 图像处理与计算机视觉
    • 滤镜、特效:对图片进行像素级的处理,如美颜、模糊、锐化等。
    • 人脸识别、物体检测:调用 OpenCV 等库,它们的核心算法是用 C++ 实现的。
  • 科学计算与模拟

    在金融、医疗、工程等领域进行复杂的数值计算。

  • 硬件交互
    • 驱动程序:为特定的硬件(如指纹传感器、专用通信芯片)编写驱动程序。
    • 通信协议:实现与外设(如通过蓝牙连接的打印机、医疗设备)的底层通信协议。

C/C++ 的优势:

  • 极致性能:编译为本地机器码,直接在 CPU 上运行,没有虚拟机开销,执行速度极快。
  • 内存控制力强:可以精细地控制内存的分配和释放,适合对内存和性能有极致要求的场景。
  • 直接操作硬件:可以访问内存地址、寄存器,与硬件直接通信。
  • 代码可复用:一个 C/C++ 库可以被 Android、iOS、桌面应用等多个平台复用。

C/C++ 的劣势:

  • 开发复杂:手动管理内存,容易产生内存泄漏、悬垂指针等严重问题。
  • 跨平台性差:代码需要为不同平台(ARM, x86)和不同架构进行编译。
  • 缺乏丰富的现成库:相比于 Java,生态系统稍弱,很多功能需要自己从零实现。
  • 调试困难:调试原生代码比调试 Java 代码要复杂得多。

如何结合使用:Java Native Interface (JNI)

JNI 是一座桥梁,它连接了 Java 世界和 C/C++ 世界。

C与Java如何协同开发Android应用?-图2
(图片来源网络,侵删)

工作流程:

  1. 在 Java 代码中声明一个 native 方法: 这个方法没有具体的实现(没有 ),只是一个声明,告诉虚拟机这个方法的实现在本地(Native)代码中。

    // MyLibrary.java
    public class MyLibrary {
        // 声明一个 native 方法
        public native String getStringFromNative();
        // 加载包含 C/C++ 实现的库文件
        static {
            System.loadLibrary("mylibrary"); // 加载 libmylibrary.so
        }
    }
  2. 使用工具生成 C/C++ 的“存根”代码: 使用 Android Studio 自带的工具或 javah 命令,根据 Java 类的 native 方法声明,生成一个对应的 C/C++ 头文件(.h),这个头文件定义了 Java 和 C/C++ 之间函数的签名。

  3. 在 C/C++ 中实现这个方法: 在生成的头文件基础上,编写 C/C++ 代码来实现 getStringFromNative 的功能,实现时需要遵循 JNI 规则来处理数据类型转换(Java 的 String 对应 C 的 jstring)。

    // mylibrary.c
    #include <jni.h>
    #include <string.h>
    #include "MyLibrary.h" // 生成的头文件
    // JNIEXPORT 和 JNICALL 是宏,用于声明导出函数
    //JNIEXPORT JNICALL jstring JNICALL Java_MyLibrary_getStringFromNative(JNIEnv *env, jobject thiz) {
    //    return (*env)->NewStringUTF(env, "Hello from C/C++!");
    //}
    // 注意:函数名有严格的命名规则:Java_包名_类名_方法名
    JNIEXPORT jstring JNICALL
    Java_com_example_myapp_MyLibrary_getStringFromNative(JNIEnv *env, jobject thiz) {
        // 创建一个 Java 字符串并返回
        return (*env)->NewStringUTF(env, "Hello from C/C++!");
    }
  4. 编译 C/C++ 代码为共享库(.so 文件): 使用 Android NDK (Native Development Kit) 工具链将 C/C++ 源代码编译成平台相关的共享库文件(如 libmylibrary.so),这个库文件会被打包进 APK 中。

    C与Java如何协同开发Android应用?-图3
    (图片来源网络,侵删)
  5. 在 Java 代码中调用: App 运行时,当调用 MyLibrary.getStringFromNative() 时,Android 运行时会找到并加载 libmylibrary.so,然后执行其中的 C/C++ 代码,并将结果返回给 Java。

数据传递与回调:

  • Java -> C/C++:基本数据类型可以直接传递,对象和数组需要通过 JNI 提供的 API 进行转换。
  • C/C++ -> Java:C/C++ 代码可以通过 JNIEnv 指针访问 Java 对象的方法和字段,从而实现回调,C/C++ 计算完一个结果后,可以调用一个 Java 的回调方法将结果传回。

现代趋势:Kotlin 与 C/C++

Google 官方推荐使用 Kotlin 作为 Android 的主要开发语言,而不是 Java,C/C++ 与 Java 的结合方式完全适用于 Kotlin。

  • Kotlin 调用 C/C++:流程和 Java 几乎完全一样,Kotlin 同样支持 external 关键字来声明 native 方法,并且与 JNI 的兼容性非常好。
  • C++ 作为 Kotlin 的扩展:由于 Kotlin 可以作为脚本语言(Kotlin Script)和更灵活的编程语言,一些开发者正在探索用 C++ 来扩展 Kotlin 的能力,尤其是在性能敏感的领域。

总结与对比

特性 Java (应用层) C/C++ (原生层)
主要角色 App 的主体:UI、业务逻辑、网络 性能增强模块:计算密集、硬件交互
性能 较好(有虚拟机开销) 极高(编译为机器码)
内存管理 自动垃圾回收 手动管理(易出错)
开发效率 (库丰富,工具链完善) 较低(复杂,调试困难)
跨平台 (一次编译,多设备运行) 差(需为不同架构编译)
硬件交互 不能(安全限制) 可以(直接访问)
调试难度 较低
结合方式 通过 JNI 调用 C/C++ 作为被 Java/Kotlin 调用的库

一句话总结:

用 Java/Kotlin 构建你的 App 的“骨架”和“血肉”(UI 和业务逻辑),当遇到 Java/Kotlin 处理不了的“硬骨头”(性能瓶颈或硬件需求)时,就用 C/C++ 来啃,并通过 JNI 这座桥梁把它们连接起来。

分享:
扫描分享到社交APP
上一篇
下一篇