杰瑞科技汇

C语言如何开发Android应用?

与常规的 Java/Kotlin 开发不同,C 开发不用于构建应用的 UI 界面,而是专注于高性能计算、代码复用、硬件访问和算法优化等领域。

C语言如何开发Android应用?-图1
(图片来源网络,侵删)

核心概念:为什么要在 Android 上用 C?

在开始之前,你必须明白 NDK 的定位:它是 Java/Kotlin 的补充,而不是替代品

  • Java/Kotlin (ART - Android Runtime):

    • 优点: 跨平台、内存管理自动、开发效率高、拥有所有 Android SDK 的 API。
    • 缺点: 性能受限于 JIT/AOT 解释和垃圾回收,无法直接访问底层硬件和操作系统特性。
  • C/C++ (Native Code):

    • 优点:
      1. 极致性能: 代码被编译成机器码,直接在 CPU 上运行,没有虚拟机开销。
      2. 硬件访问: 可以直接调用 Linux 内核 API,访问摄像头传感器、GPS、OpenGL ES 等。
      3. 代码复用: 可以将现有的 C/C++ 库(如游戏引擎、音视频编解码库)直接集成到 Android 应用中。
      4. 多线程: 可以创建不受 Dalvik/ART 虚拟机管理的原生线程,适合计算密集型任务。
    • 缺点:
      1. 开发复杂: 没有自动内存管理,需要手动管理内存,容易引发崩溃。
      2. 平台相关: 需要为不同的 CPU 架构(ARM, x86)分别编译。
      3. 调试困难: 崩溃时可能只看到一个 native crash 的地址,难以定位。
      4. API 有限: 不能直接调用 Android Framework 的 Java API。

环境准备

在开始之前,请确保你的开发环境已经配置好。

C语言如何开发Android应用?-图2
(图片来源网络,侵删)
  1. 安装 Android Studio: 这是最主要的开发工具。
  2. 安装 NDK 和 CMake:
    • 打开 Android Studio。
    • 进入 Tools -> SDK Manager
    • 切换到 SDK Tools 标签页。
    • 勾选 NDK (Side by side)CMake
    • 点击 ApplyOK 进行安装。

开发流程:从零开始创建一个 C 库并调用它

我们将通过一个最经典的例子:在 Java 中调用 C 代码实现两个整数相加

步骤 1:创建一个支持 C/C++ 的新项目

  1. 打开 Android Studio,选择 File -> New -> New Project...
  2. 选择 Native C++ 模板(这是最简单的方式,它会自动帮你配置好所有东西),点击 Next
  3. 输入你的应用名称(MyCApp),选择包名和保存位置,点击 Next
  4. 选择 C++ 标准(C++17 是一个不错的选择),点击 Finish
  5. Android Studio 会为你创建一个项目,并包含一个示例的 native-lib.cpp 文件。

步骤 2:理解项目结构

创建项目后,你会发现几个关键的变化:

  • app/src/main/cpp/: 这是存放你的 C/C++ 源代码的地方。
    • native-lib.cpp: 你的第一个 C++ 文件,这里已经有一个示例函数 stringFromJNI()
  • app/src/main/java/com/yourcompany/mycapp/: Java/Kotlin 代码。
    • MainActivity.java: 包含加载库和调用 native 方法的代码。
  • CMakeLists.txt: 这是 C/C++ 的构建脚本,它告诉 CMake 如何编译你的 native 代码,生成一个共享库(.so 文件)。
  • build.gradle (Module: app): 这个文件被修改,包含了 NDK 和 CMake 的路径配置。

步骤 3:编写 C/C++ 代码

打开 app/src/main/cpp/native-lib.cpp,我们将修改它。

#include <jni.h>
#include <string>
// extern "C" 是必须的,它告诉 C++ 编译器使用 C 语言的链接方式,
// 这样 Java 才能通过函数名找到这个函数。
extern "C" JNIEXPORT jstring JNICALL
Java_com_yourcompany_mycapp_MainActivity_addNumbers(
        JNIEnv* env,
        jobject /* this */,
        jint a,
        jint b) {
    // 执行加法运算
    int result = a + b;
    // 将结果转换成字符串
    std::string result_str = "The result is: " + std::to_string(result);
    // JNI 函数,将 C++ 的 std::string 转换成 Java 的 String 对象
    return env->NewStringUTF(result_str.c_str());
}

代码解释:

C语言如何开发Android应用?-图3
(图片来源网络,侵删)
  • extern "C": 这是 JNI 的关键,Java 调用 native 方法时,会查找一个特定的函数名。extern "C" 确保了函数名不会被 C++ 的名称修饰(Name Mangling)所改变。
  • JNIEXPORT / JNICALL: 这是 JNI 的宏定义,表示该函数可以被外部调用。
  • Java_com_yourcompany_mycapp_MainActivity_addNumbers:
    • 这是 JNI 的函数命名规则:Java_ + 包名(用下划线 _ 替换点 ) + _ + 类名 + _ + 方法名。
    • MainActivity 会调用这个函数。
  • JNIEnv* env: 指向 JNI 环境的指针,通过它你可以调用 JNI 函数来操作 Java 对象。
  • jobject this: 指向调用该 native 方法的 Java 对象(这里是 MainActivity 的实例)。
  • jint a, jint b: jint 是 JNI 中对 Java int 类型的映射,这两个参数来自 Java 方法。

步骤 4:修改 CMakeLists.txt

这个文件告诉 CMake 如何编译你的代码,默认情况下,它已经包含了你的 native-lib.cpp,如果你添加了新的 .cpp 文件,需要在这里声明。

打开 app/CMakeLists.txt,你会看到类似这样的内容:

# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.18.1)
# project 定义项目名称和 C++ 标准
project("mycapp")
# 添加一个库
# add_library(库名称 SHARED 源文件列表)
add_library(
        native-lib
        SHARED
        native-lib.cpp)
# 找到 Android 的日志库
find_library(
        log-lib
        log)
# 将库链接到你的 native-lib
# target_link_libraries(库名称 要链接的库)
target_link_libraries(
        native-lib
        ${log-lib})

这里 add_library 是核心,它定义了一个名为 native-lib 的共享库,其源文件是 native-lib.cpp

步骤 5:在 Java/Kotlin 中调用 C 代码

我们需要在 MainActivity.java 中声明 native 方法,并加载库。

打开 app/src/main/java/com/yourcompany/mycapp/MainActivity.java

package com.yourcompany.mycapp;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Button;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
    // 加载名为 "native-lib" 的库
    // 这个库名必须和 CMakeLists.txt 中 add_library 的第一个参数一致
    static {
        System.loadLibrary("native-lib");
    }
    // 声明一个 native 方法
    // 函数签名必须和 C++ 中的函数名完全匹配(去掉 Java_ 前缀)
    // 参数和返回类型使用 JNI 类型
    public native String addNumbers(int a, int b);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 获取 UI 控件
        TextView tv = findViewById(R.id.sample_text);
        Button button = findViewById(R.id.my_button);
        // 设置按钮点击事件
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 调用 C 函数
                String result = addNumbers(10, 25);
                // 显示结果
                tv.setText(result);
                Toast.makeText(MainActivity.this, "C code called!", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

代码解释:

  • System.loadLibrary("native-lib"): 这行代码会去加载 libnative-lib.so 这个库,这个 .so 文件是由 CMake 在编译时生成的。
  • public native String addNumbers(int a, int b);: 这就是 Java 声明。native 关键字告诉 JVM 这个方法的实现不在 Java 中,而是在本地代码里,参数和返回类型与 C++ 函数对应。
  • addNumbers(10, 25): 像调用普通 Java 方法一样调用它,参数会自动转换成 JNI 类型传递给 C 函数,C 函数的返回值也会自动转换成 Java 的 String

步骤 6:运行应用

  1. 确保你的模拟器或真机已连接。
  2. 点击 Android Studio 的绿色 "Run" 按钮。
  3. 应用启动后,点击按钮,你会看到文本区域显示 "The result is: 35",同时会弹出一个 Toast 提示。

恭喜!你已经成功地在 Android 应用中调用了 C 语言代码!


C 开发进阶与资源

数据类型映射

Java 和 C/C++ 之间的数据类型需要通过 JNI 进行转换。

Java 类型 JNI 类型 C/C++ 类型
byte jbyte signed char
short jshort short
int jint int
long jlong long long
float jfloat float
double jdouble double
char jchar unsigned short
boolean jboolean unsigned char (true=1, false=0)
String jstring -
Object jobject -
Class jclass -

调试 Native 代码

调试 C/C++ 代码比调试 Java 困难,但 Android Studio 提供了强大的支持。

  1. 在 C/C++ 代码的行号左侧单击,设置断点(红点)。
  2. 在运行配置中,确保 Debug 按钮旁边选择了 native
  3. 点击 "Debug" 按钮。
  4. 程序会停在断点处,你可以查看变量、单步执行等。

推荐学习资源

  1. 官方文档 (必读):

  2. 书籍:

    • 《Android NDK Beginner's Guide》: 非常适合入门,讲解清晰。
    • 《The Android Developer's Cookbook》: 其中章节也涉及 NDK 开发。
  3. 在线教程与博客:

    • ProAndroidDev (Medium 专栏): 经常有高质量的 NDK 相关文章。
    • Stack Overflow: 解决具体问题的最佳去处,搜索时记得加上 android-ndk
  4. GitHub 开源项目:

    • FFmpeg Android: 学习如何集成和编译复杂的 C 库。
    • OpenCV Android: 学习如何进行高性能的图像处理。
    • 各种游戏引擎的源码: 如 Godot, Cocos2d-x 等,它们大量使用 NDK。

使用 C 开发 Android 应用是一项专业技能,它将你带入了更底层的世界,虽然入门比 Java/Kotlin 难,但它在游戏、音视频、AR/VR 等领域是不可或缺的。

学习路径建议:

  1. 掌握 JNI: 理解 Java 和 C 之间如何交互、数据如何传递、如何处理异常。
  2. 精通 CMake: 学会如何组织和管理你的 C/C++ 项目。
  3. 学习 Android NDK 特有的 API: 如传感器访问、OpenGL ES 绑定等。
  4. 实践: 从集成一个简单的第三方库开始,zlib,逐步挑战更复杂的项目。

祝你学习顺利!

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