jni的使用,so库的生成与使用。从0到1记录

55 阅读2分钟

思考:把Android里的关键逻辑下沉到Native。比如核心、敏感、或者性能要求高的逻辑,从Java/Kotlin层挪到C/C++(so 库)里,通过JNI去调用。

  • 提高了逆向成本(防破解、防篡改)
  • 提升了性能(某些计算密集型场景)
  • 隔离了关键算法(让 Java 层变成“壳”)

一、JNI的使用

  1. 配置NDK + CMake (Android Studio 里 SDK Tools 勾选 NDK、CMake)

7788.png 2. 构建cpp目录

app/src/main/cpp/
    ├── CMakeLists.txt
    └── native-lib.cpp

3. 构建 CMakeLists.txt

cmake_minimum_required(VERSION 3.22.1)
project("yinyin-lib")  # 库名,对应 System.loadLibrary("yinyin-lib")

add_library(
        ${CMAKE_PROJECT_NAME}
        SHARED
        native-lib.cpp  # 源码路径
)

find_library(
        log-lib
        log
)

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

4. 构建 native-lib.cpp

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

// extern "C" 防止 C++ 名字被改编
extern "C" {

    // ==================== 示例 1:无参数,返回 String ====================
    JNIEXPORT jstring JNICALL
    Java_com_example_use_1jni_1and_1so_MainActivity_helloJNI(JNIEnv* env, jobject thiz) {
        std::string result = "Hello from C++";
        return env->NewStringUTF(result.c_str());
    }

    // ==================== 示例 2:带参数,返回 int ====================
    JNIEXPORT jint JNICALL
    Java_com_example_use_1jni_1and_1so_MainActivity_addNumbers(JNIEnv* env, jobject thiz, jint a, jint b) {
        return a + b;
    }

    // ==================== 示例 3:带 String 参数,返回 String ====================
    JNIEXPORT jstring JNICALL
    Java_com_example_use_1jni_1and_1so_MainActivity_reverseString(JNIEnv* env, jobject thiz, jstring input) {
        const char* str = env->GetStringUTFChars(input, 0); //java string 转 c string
        std::string s(str); // c string 转 c++ string
        env->ReleaseStringUTFChars(input, str); // 释放 c string.  参数1:原始的 jstring,参数2:要释放的 C 字符串指针

        std::reverse(s.begin(), s.end()); // 翻转string,并将值赋予s
        return env->NewStringUTF(s.c_str()); // 创建新的java string并返回
    }

} // extern "C"

5. 配置build.gradle中的NDK和CMake

ndk

android{
    defaultConfig{
        ndk {
            abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
        }
    }
}

cmake

android{
    defaultConfig{
        externalNativeBuild {
            cmake {
                cppFlags += ""
            }
        }
    }
}
android {
    externalNativeBuild {
        cmake {
            path = file("src/main/cpp/CMakeLists.txt")
        }
    }
}

  1. 再写 Kotlin external 接口 举个栗子
init {
    System.loadLibrary("yinyin-lib")
}

external fun hello(): String

业务层调用hello()

此时已经跑通了~ Gradle → CMake → so → JVM → JNI → C++

  1. 再开始写真正的业务 JNI 接口 native-lib.cpp

举个栗子:校验、算法、加密、核心逻辑。

此时只是: 把 C++ 的实现变复杂, Kotlin 侧只改方法声明。

  1. 最后才是“业务层设计”

举个栗子

object NativeBridge {
    external fun checkLicense(key: String): Boolean
}

JNI小结

  1. NDK / CMake 环境 ↓
  2. CMakeLists.txt ↓
  3. native-lib.cpp(最小可运行函数) ↓
  4. System.loadLibrary ↓
  5. external fun ↓
  6. JNI 能跑通 ↓
  7. 扩展为真实业务

二、so的生成与使用

生成so:

  • 配置完CMake会编译生成出来so。
  • run只会生成当前设备ABI的so
  • Build APK(s)或 assembleDebug 会生成所有ABI的so
  • 生成的so位置在:\build\intermediates\cxx 或者 \build\intermediates\merged_native_libs\debug\mergeDebugNativeLibs\out\lib

使用so:

  1. 创建jniLibs。
app/
 └── src/
     └── main/
         └── jniLibs
  1. 放置so库,目录结构如下:
app/
 └── src/
     └── main/
         └── jniLibs/
             ├── arm64-v8a/
             │    └── libyinyin-lib.so
             ├── armeabi-v7a/
             │    └── libyinyin-lib.so
             ├── x86/
             │    └── libyinyin-lib.so
             └── x86_64/
                  └── libyinyin-lib.so
  1. 模块build.gradle配置jniLibs。
android {
    sourceSets {
        getByName("main") {
            jniLibs.srcDirs("src/main/jniLibs")
        }
    }
}
  1. 业务层调用。

加载so库到进程中->配置方法->调用方法。 举个栗子:

System.loadLibrary("yinyin-lib")
external fun helloJNI(): String?