C语言开发Android可行吗?如何实现?

99ANYc3cd6
预计阅读时长 25 分钟
位置: 首页 C语言 正文

Android 应用开发的主流语言是 Java 和 Kotlin,它们运行在 Dalvik/ART 虚拟机上,而 C 语言(以及 C++)不能直接用来开发一个完整的、可运行的 Android App UI 界面。

C 语言开发Android
(图片来源网络,侵删)

我们为什么要在 Android 开发中用 C 语言呢?答案是:性能、复用和访问底层


为什么要在 Android 中使用 C/C++?

  1. 性能优化

    • 计算密集型任务:对于游戏物理引擎、音视频编解码、图像/信号处理、科学计算等,C/C++的执行效率远高于 Java/Kotlin,它们可以直接操作内存,没有虚拟机的额外开销。
    • 减少 GC 压力:Java/Kotlin 的自动垃圾回收机制在处理大量对象时可能会造成卡顿,C/C++ 手动管理内存,可以精确控制,避免 GC 停顿带来的性能问题。
  2. 代码复用

    • 很多成熟的、高性能的库都是用 C/C++ 编写的,
      • FFmpeg:音视频处理库。
      • OpenCV:计算机视觉库。
      • OpenGL ES:3D 图形渲染 API(虽然 API 是 C 风格,但驱动是 C++ 的)。
      • TensorFlow Lite:移动端机器学习推理引擎。
    • 通过在 Android 中集成这些 C/C++ 库,可以避免重复造轮子,快速实现高级功能。
  3. 访问硬件和底层系统

    C 语言开发Android
    (图片来源网络,侵删)
    • Android 系统本身是基于 Linux 内核的,其很多硬件驱动和系统服务都是用 C/C++ 编写的。
    • 当你需要访问特定的硬件功能(如相机传感器、GPS 芯片寄存器)或调用一些不通过 Java API 暴露的底层系统功能时,就需要通过 C/C++ 来实现。

在 Android 中使用 C/C++ 的主要方式

主要有两种方式,它们适用于不同的场景:

  1. JNI (Java Native Interface):传统的、经典的“胶水”层技术。
  2. NDK (Native Development Kit):Google 官方提供的工具集,用于简化 JNI 的使用和原生代码的编译。

NDK 是建立在 JNI 之上的一个更高级的工具集。

JNI (Java Native Interface)

JNI 是一个编程框架,它允许运行在虚拟机(JVM)中的 Java 代码与其它语言(如 C/C++)编写的应用程序进行交互。

工作流程:

C 语言开发Android
(图片来源网络,侵删)
  1. Java 端:在 Java 或 Kotlin 代码中,声明一个 native 方法。
  2. 编译生成头文件:使用 javac 编译 Java 代码,然后用 javah 工具(或新版 JDK 的 javac -h)生成一个 C/C++ 头文件(.h),这个头文件包含了 Java native 方法的 C 函数声明。
  3. C/C++ 端:实现头文件中声明的 C/C++ 函数,这个函数的签名必须与 JNI 规范严格匹配,以便虚拟机能找到它。
  4. 加载库:在 Java 代码中,使用 System.loadLibrary("库名称") 来加载编译好的 C/C++ 动态链接库(.so 文件)。
  5. 交互:在 C/C++ 函数中,你可以通过 JNI 提供的 API 来操作 Java 对象、调用 Java 方法,反之亦然。

JNI 的缺点:

  • 繁琐:需要手动编写大量胶水代码,数据类型转换(如 Java 的 String 和 C 的 char*)非常麻烦。
  • 易出错:手动管理 JNI 代码很容易导致内存泄漏或崩溃。
  • 维护困难:代码结构复杂,难以维护。

NDK (Native Development Kit)

NDK 是一个工具集,它极大地简化了在 Android 上使用 C/C++ 的过程,它不仅仅包含 JNI,还提供了编译器、构建系统、调试工具等。

NDK 的核心优势:

  1. 简化开发:NDK 提供了更高级的 API(如 JNIHelper),让你可以用更现代的 C++ 风格进行开发,而不是手写繁琐的 JNI 代码。
  2. 构建系统:集成 CMakendk-build,你可以像在桌面端开发 C++ 项目一样管理原生代码的编译、链接,自动化程度高。
  3. 预构建库:NDK 自带了许多预编译好的库,如 libc++ (C++ 标准库)、libandroid (Android 平台支持)、OpenSSL 等,你只需在 CMakeLists.txt 中引用即可。
  4. ABI 支持:NDK 可以轻松为不同的 CPU 架构(如 armeabi-v7a, arm64-v8a, x86, x86_64)编译原生库,确保应用在不同设备上都能高效运行。
  5. C++ 标准:支持现代 C++ 标准(如 C++11, C++17),让你可以使用更强大、更安全的 C++ 特性。

现代 Android 开发中,几乎总是使用 NDK 来集成 C/C++ 代码,而不是直接手写 JNI。


实战:如何使用 Android Studio 和 NDK 集成 C++ 代码

这是目前最主流、最推荐的方式。

步骤 1:配置项目

  1. 创建一个新的 Android 项目。

  2. build.gradle (Module: app) 文件中,启用 viewBinding (可选) 并配置 NDK。

    // build.gradle (Module: app)
    android {
        // ... 其他配置
        defaultConfig {
            // ... 其他配置
            externalNativeBuild {
                cmake {
                    // 可以指定 C++ 标准
                    cppFlags "-std=c++17"
                    // 可以在这里传递参数给 CMake
                    // arguments "-DANDROID_STL=c++_shared"
                }
            }
            // 指定需要支持的 CPU 架构
            ndk {
                abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86'
            }
        }
        // 配置 CMake 构建文件的路径
        externalNativeBuild {
            cmake {
                path "src/main/cpp/CMakeLists.txt"
                version "3.18.1" // 使用你安装的 CMake 版本
            }
        }
    }
    dependencies {
        // ... 其他依赖
    }

步骤 2:创建 C++ 代码

  1. 在 Android Studio 中,右键点击你的模块 -> New -> Folder -> CPP Folder,这会自动在 src/main/ 下创建一个 cpp 目录,并关联好 CMakeLists.txt

  2. cpp 目录下,创建一个 C++ 源文件,native-lib.cpp

    // native-lib.cpp
    #include <jni.h>
    #include <string>
    // extern "C" 告诉编译器这段代码是 C 风格的,避免 C++ 的名称修饰
    extern "C" JNIEXPORT jstring JNICALL
    Java_com_example_myapp_MainActivity_stringFromNative(
            JNIEnv* env,
            jobject /* this */,
            jstring inputString) { // 这里我们让 Java 传入一个字符串
        // 将 Java 字符串转换为 C++ 字符串
        const char* c_str = env->GetStringUTFChars(inputString, nullptr);
        std::string hello = "Hello from C++! You sent: ";
        hello += c_str;
        // 释放资源
        env->ReleaseStringUTFChars(inputString, c_str);
        // 将 C++ 字符串转换回 Java 字符串并返回
        return env->NewStringUTF(hello.c_str());
    }
    • Java_com_example_myapp_MainActivity_stringFromNative:这是 JNI 函数的命名规则。
      • Java_: 固定前缀。
      • com_example_myapp: 包名,用下划线 _ 代替点 。
      • MainActivity: 类名。
      • stringFromNative: Java 中声明的 native 方法名。
    • *`JNIEnv`**:指向 JNI 环境的指针,通过它可以调用 JNI API。
    • jobject:指向当前 Java 对象(这里是 MainActivity)的引用。

步骤 3:配置 CMake (CMakeLists.txt)

cpp/CMakeLists.txt 文件中,添加你的原生库。

# CMakeLists.txt
# 设置 C++ 标准
cmake_minimum_required(VERSION 3.18.1)
# 定义项目名称
project("myapp")
# 添加一个原生共享库
add_library(
        native-lib        # 库名
        SHARED            # 共享库 (.so)
        native-lib.cpp)   # 源文件列表
# 查找并链接 Android 的日志库,方便在 C++ 中打印日志
find_library(
        log-lib
        log)
# 将你的库链接到 log-lib
target_link_libraries(
        native-lib
        ${log-lib})

步骤 4:在 Java/Kotlin 中调用

  1. 在你的 MainActivity.java (或 MainActivity.kt) 中声明 native 方法并加载库。

    // MainActivity.java
    package com.example.myapp;
    import androidx.appcompat.app.AppCompatActivity;
    import android.os.Bundle;
    import android.widget.TextView;
    import android.widget.Button;
    public class MainActivity extends AppCompatActivity {
        // 声明一个 native 方法
        public native String stringFromNative(String input);
        static {
            // 加载编译好的原生库
            System.loadLibrary("native-lib");
        }
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            TextView tv = findViewById(R.id.sample_text);
            Button button = findViewById(R.id.my_button);
            button.setOnClickListener(v -> {
                // 调用 native 方法
                String result = stringFromNative("Hello Android!");
                tv.setText(result);
            });
        }
    }
  2. res/layout/activity_main.xml 中添加一个 TextView 和一个 Button

步骤 5:编译和运行

点击 Android Studio 的 "Run" 按钮,Android Studio 会自动:

  1. 调用 CMake 来编译 native-lib.cpp,生成 .so 文件。
  2. .so 文件打包到 APK 中。
  3. 运行 App,点击按钮,你会看到 TextView 中显示从 C++ 返回的字符串。

总结与最佳实践

特性 JNI NDK
定位 一个编程接口/规范 一个完整的工具集
易用性 繁琐,需要大量手动胶水代码 简单,提供了高级 API 和构建系统
核心 手动实现 native 方法的 C/C++ 函数 集成 CMake,管理原生代码的编译和链接
适用场景 非常小的、一次性的性能优化;学习 JNI 原理 绝大多数场景,特别是集成现有库或开发复杂原生模块
C++ 支持 较弱,容易出错 强大,支持现代 C++ 标准

最佳实践:

  1. 优先使用 NDK:除非你有非常特殊的需求,否则请始终使用 NDK 来管理你的 C/C++ 代码。
  2. CMake 是你的朋友:熟练掌握 CMakeLists.txt 的编写,它能让你高效地管理复杂的原生项目。
  3. 封装与解耦:尽量将 C/C++ 代码封装成独立的、无状态的模块,通过定义清晰的接口(在 Java/Kotlin 端),减少 C++ 和 Java 之间的频繁交互,降低耦合度。
  4. 错误处理:原生代码的崩溃(如段错误)不会像 Java 异常那样被捕获,会导致整个 App 崩溃,务必做好原生代码的错误检查和日志记录。
  5. 调试:Android Studio 提供了强大的原生代码调试功能,你可以像调试 Java 代码一样设置断点、单步执行 C/C++ 代码,这对于排查问题至关重要。

虽然 C 语言不能独立开发一个完整的 Android App,但它是 Android 生态中不可或缺的一部分,通过 NDK,你可以将 C/C++ 的强大性能和丰富库生态无缝地集成到你的 Java/Kotlin 应用中,打造出既功能强大又性能卓越的移动应用。

-- 展开阅读全文 --
头像
dede手机站wap怎么关闭?
« 上一篇 昨天
C语言Hello World程序如何正确编写与运行?
下一篇 » 昨天

相关文章

取消
微信二维码
支付宝二维码

目录[+]