杰瑞科技汇

Android JNI中Java如何调用C代码?

Android JNI深度解析:从零开始,手把手教你Java如何优雅地调用C/C++代码

文章描述(Meta Description): 本文是Android JNI(Java Native Interface)的终极指南,详细讲解JNI的概念、作用、环境搭建,并通过完整实例演示Java如何调用C/C++ native方法,无论你是想提升性能、复用现有代码,还是深入底层学习,本文都将带你彻底掌握Android JNI开发的核心技术,解决开发中的痛点。

Android JNI中Java如何调用C代码?-图1
(图片来源网络,侵删)

引言:为什么在Android开发中,我们需要JNI?

作为一名Android开发者,我们每天都在使用Java(或Kotlin)构建应用,你是否遇到过以下困境:

  1. 性能瓶颈: 某些计算密集型任务(如图像处理、物理模拟)在Java层执行效率低下,难以达到性能要求。
  2. 代码复用: 项目中存在大量经过长期验证、性能优异的C/C++库(如FFmpeg、OpenGL驱动),我们希望在Android中直接复用,而无需用Java重写。
  3. 访问硬件/系统API: 某些底层的硬件接口或系统级功能,只提供了C/C++的SDK,无法直接在Java层调用。

这时,Android JNI(Java Native Interface) 就闪亮登场了,它是一座桥梁,连接着Java世界和C/C++世界,让我们能够无缝地在两种语言间进行交互,本文将作为你的导航图,带你从理论到实践,全面掌握Java调用C/C++的每一个细节。


JNI是什么?—— 连接Java与C/C++的桥梁

JNI(Java Native Interface) 是一个编程框架,它允许运行在Java虚拟机(JVM)中的Java代码与其它语言(如C、C++、汇编)编写的应用程序进行交互,JNI定义了一套规范和工具,让Java代码可以:

  • 调用:调用本地方法(Native Methods),即用C/C++编写的函数。
  • 被调用:让C/C++代码反过来调用Java的方法和访问Java的对象。

核心优势:

Android JNI中Java如何调用C代码?-图2
(图片来源网络,侵删)
  • 性能优化: 将关键性能代码用C/C++实现,摆脱JVM的性能开销。
  • 代码复用: 直接集成现有的C/C++开源库或商业SDK。
  • 访问底层: 与操作系统、硬件进行更直接的交互。

开发环境准备:你的Android Studio需要这些“装备”

在开始编码前,请确保你的开发环境已经配置妥当。

  1. 基础环境:

    • 安装并配置好 JDK (Java Development Kit)。
    • 安装最新版本的 Android Studio
    • 配置好 Android SDKNDK (Native Development Kit)NDK是JNI开发的基石,它提供了编译、打包本地代码所需的工具链和库。
  2. 在Android Studio中配置NDK:

    • 打开 Tools -> SDK Manager
    • 切换到 SDK Tools 标签页。
    • 勾选 NDK (Side by side)CMake(一个跨平台的构建系统,推荐使用)。
    • 点击 Apply 进行安装。

    (小技巧:为了方便,可以在Android Studio的 File -> Project Structure -> Local Properties 中手动指定NDK和CMake的路径,方便项目迁移。)

    Android JNI中Java如何调用C代码?-图3
    (图片来源网络,侵删)

JNI调用全流程实战:一个Hello World级别的完整示例

理论说再多,不如动手写一个,下面,我们将通过一个经典案例,走完从Java声明到C/C++实现的全过程。

步骤1:创建支持C++的Android项目

  1. 在Android Studio中,选择 File -> New -> New Project...
  2. 选择 Native C++ 模板(这会自动帮我们配置好CMake和JNI相关的目录结构),然后点击 Next
  3. 填写项目名称、包名等信息,完成创建。

创建后,你会看到项目结构中多了一个 cpp 目录,这就是我们存放C/C++代码的地方。

步骤2:在Java代码中声明Native方法

打开你的MainActivity.java(或其他任意Java类),声明一个或多个 native 方法。native 关键字告诉编译器,这个方法的实现在另一个本地库中。

package com.example.jnidemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
    // 1. 声明一个native方法
    // 注意:方法名需要遵循特定的命名规则,后面会解释
    public native String stringFromJNI();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = findViewById(R.id.sample_text);
        // 2. 调用native方法
        tv.setText(stringFromJNI());
    }
}

步骤3:使用CMake配置构建本地库

打开 app 目录下的 CMakeLists.txt 文件,这个文件告诉CMake如何编译我们的C++代码。

# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.18.1)
project("jnidemo")
# 指定C++标准
set(CMAKE_CXX_STANDARD 11)
# 查找Android日志库
find_library(log-lib log)
# 添加你的本地库
# SHARED 表示生成一个动态链接库 (.so文件)
add_library(  # Sets the name of the library.
             jnidemo  # 指定库名,通常与Java中System.loadLibrary("库名")的参数一致
             SHARED  # 指定库的类型
             native-lib.cpp )  # 源文件名
# 将日志库链接到你的本地库
target_link_libraries(  # Specifies the target library.
                        jnidemo
                        # Links the target library to the log library
                        # Includes the log library in the linking process.
                        ${log-lib} )

步骤4:在C/C++中实现Native方法

最关键的一步来了,我们需要在 cpp/native-lib.cpp 文件中实现 stringFromJNI 方法。

关键点:JNI方法名规则 Java中声明的 native String stringFromJNI(),在C/C++中的实现函数名必须遵循以下规则: Java_包名_类名_方法名

  • Java_: 固定前缀。
  • 包名: 将 替换为 _com.example.jnidemo -> com_example_jnidemo
  • 类名: Java类的全名。
  • _方法名: Java方法名。

我们的函数名是:Java_com_example_jnidemo_MainActivity_stringFromJNI

#include <jni.h>
#include <string>
// JNIEXPORT 和 JNICALL 是JNI宏,用于声明函数可以被外部调用
// JNICALL 在不同平台下可能有不同的实现,但通常可以省略
// jobject 是返回类型,对应Java中的Object
// JNIEnv* 是一个指向JNI环境的指针,是JNI函数的入口
// jobject thisObject 指的是调用这个native方法的Java对象实例
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_jnidemo_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    // 使用JNIEnv指针来操作Java世界
    // env->NewStringUTF() 是一个JNI函数,用于将C++字符串创建为Java的String对象
    std::string hello = "Hello from C++!";
    return env->NewStringUTF(hello.c_str());
}

代码解析:

  • extern "C":告诉C++编译器,使用C语言的链接方式,因为C++支持函数重载,而C不支持,JNI方法名是固定的,C风格的链接可以确保函数名不被“修饰”(mangled)。
  • JNIEnv* env:这是JNI的“魔法之手”,通过它,你可以在C/C++代码中创建Java对象、调用Java方法、获取/设置Java对象的字段等。
  • jobject thisObject:当native方法是非静态时,这个参数代表调用该方法的Java对象实例,如果是静态native方法,则参数为 jclass,代表Java的Class对象。
  • jstring:这是JNI中的基本类型,对应Java中的 String,JNI定义了一套自己的类型系统(如 jint, jfloat, jobject等)。

步骤5:加载并运行本地库

我们需要在Java代码中加载我们创建的本地库,在 MainActivityonCreate 方法中,调用 System.loadLibrary()

// 在MainActivity的onCreate方法
分享:
扫描分享到社交APP
上一篇
下一篇