Android 12 编译 JNI 库的初探

在 Android 应用开发中,使用 JNI(Java Native Interface)可以让我们在 Java 中调用 C/C++ 代码,从而提升性能或者利用某些特定的系统功能。然而,组合 Java 和 C/C++ 需要一些设置和配置,特别是在 Android 12 环境下。本文将详细介绍如何编译 JNI 库,并提供代码示例。

1. JNI 库结构

一个标准的 JNI 库通常包含以下几个部分:

  • C/C++ 源代码
  • Java 接口类
  • 构建配置文件(如 CMakeLists.txt 或 Android.mk)

1.1 目录结构示例

MyApplication/
├── app/
│   ├── src/
│   │   ├── main/
│   │   │   ├── cpp/
│   │   │   │   └── native-lib.cpp
│   │   │   ├── java/
│   │   │   │   └── com/example/myapplication/
│   │   │   │       └── MainActivity.java
│   │   │   └── CMakeLists.txt
└── build.gradle

在上述结构中,native-lib.cpp 是我们的 JNI 实现代码,MainActivity.java 是调用 JNI 方法的 Java 部分,而 CMakeLists.txt 是构建过程的配置文件。

2. 编写 C++ 代码

首先,我们需要编写 C++ 代码并声明 JNI 方法。以下是一个简单的 C++ 示例,它定义了一个返回字符串的方法。

2.1 C++ 示例代码

native-lib.cpp 中:

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(JNIEnv* env, jobject) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

这里,我们使用 extern "C" 来保证 C++ 的命名不会被修改,并创建了一个 JNI 方法 stringFromJNI。这个方法返回一个 jstring,即 Java 字符串。

3. Java 接口类

接下来,我们需要 Java 类来调用这个 JNI 方法。在 MainActivity.java 文件中,我们需要添加加载库和调用方法的代码。

3.1 Java 示例代码

package com.example.myapplication;

import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView textView = findViewById(R.id.sample_text);
        textView.setText(stringFromJNI());
    }

    public native String stringFromJNI();
}

在这个 Java 类中,System.loadLibrary("native-lib"); 语句用于加载我们的 JNI 库,而 stringFromJNI() 是声明的 JNI 方法,将返回 C++ 返回的字符串。

4. 配置 CMakeLists.txt

为了让 Android Studio 知道如何构建 JNI 库,我们需要配置 CMakeLists.txt 文件。以下是一个基本的配置:

4.1 CMakeLists.txt 示例代码

cmake_minimum_required(VERSION 3.4.1)

add_library(native-lib SHARED native-lib.cpp)

find_library(log-lib log)

target_link_libraries(native-lib ${log-lib})

在以上配置中,我们指明了创建一个名为 native-lib 的共享库,同时连接了 Android 的日志库。

5. 构建与运行

完成所有代码后,你可以通过 Android Studio 的 Build 功能来构建项目。这时,C++ 代码将被编译为 JNI 库,并与 Java 代码一起打包到 APK 中。

6. 关系图

为了更好地理解 JNI 的工作机制,下面用 mermaid 语法展示 JNI 与 Android 组件之间的关系图:

erDiagram
    JNI {
        +string stringFromJNI()
    }
    MainActivity {
        +void onCreate()
        +String stringFromJNI()
    }
    
    MainActivity ||--o{ JNI : calls

在这个关系图中,我们可以看到 MainActivity 类通过 JNI 方法 stringFromJNI 进行交互。

7. 状态图

下面是用 mermaid 语法展示的应用在 JNI 调用中的状态图:

stateDiagram
    [*] --> Idle
    Idle --> CallingJNI
    CallingJNI --> Returning
    Returning --> Idle

在状态图中,应用首先处于 Idle(空闲)状态,当调用 JNI 方法后,它进入 CallingJNI 状态,然后返回到 Idle 状态。

8. 结论

本文简要介绍了如何在 Android 12 中编译 JNI 库的基础知识。通过示例代码和图示,我们了解到涉及 C/C++ 代码与 Java 代码交互的过程。随着 Android 应用日益复杂,掌握 JNI 的使用能够帮助开发者在 Android 平台上更高效地运行特定任务。

编写和调试 JNI 代码可能会遇到许多挑战,但理解其工作原理和构建流程无疑是克服这些挑战的第一步。希望这篇文章对您在 Android 开发过程中使用 JNI 有所帮助。