Android Studio 3.5 JNI编程与动态注册

本文介绍了Android的JNI与SO库的概念,详细阐述了如何在Android Studio 3.5.3中搭建JNI开发环境,创建C++ Native工程。接着重点讲解了静态注册与动态注册的区别,尤其是动态注册的四个步骤:参数映射表、注册native方法、载入JNI方法和实现的Native方法。动态注册避免了静态注册中繁琐的函数命名规则和运行时查找的低效问题。
摘要由CSDN通过智能技术生成

Android的JNI、so与JAVA

Android程序分2层,java层与native层,Java层就是Java代码编译为dex文件的。而native层则是c++代码编译为so动态库。两者使用jni(java native interface)来进行链接。相比于java,native层安全性更加高,隐蔽性更好,某种情况下效率更高。国内的加密与检测一般都放在native层中进行。

Jni分为静态和动态注册两种方式,Android Studio默认的工程是静态注册。

环境搭建:

Android Studio 3.5.3

打开SdkManager下载LLDB、NDK、CMake。
在这里插入图片描述
Project Structure中配置NDK。
在这里插入图片描述

建立一个C++Native工程

[File] -> [New] ->[New Project…],选择Native C++模版。
在这里插入图片描述
点[Next],其余默认就行。完成后工程结构如下:
在这里插入图片描述
红色框中为自动生成。

蓝色框内为手动添加,目的是熟悉CMake工具构建过程,可以不添加。

CMakeLists.txt文件是CMake构建需要的文件,文件内容解析如下:

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

# 创建和命名库,将其设置为静态或共享,并提供到其源代码的相对路径。
# 您可以定义多个库,然后CMake为您构建它们。Gradle自动将共享库打包到你的APK中。

add_library( # Sets the name of the library.
        native-lib  # 设置库的名字叫native-lib

        # Sets the library as a shared library.
        SHARED  # 设置为共享库

        # Provides a relative path to your source file(s).
        # 源码文件的相对路径
        native-lib.cpp
        one-lib.cpp  # 自己添加的CPP文件
        two-lib.cpp) # 自己添加的CPP文件

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

# 搜索指定的预构建库并将路径存储为变量。
# 由于默认情况下CMake的搜索路径中包含系统库,因此只需指定要添加的公共NDK库的名称。
# CMake在完成构建之前验证该库是否存在。

# 将log库存储为名叫log-lib的变量
find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

# 在上方定义好的native-lib库中引入log库
target_link_libraries( # Specifies the target library.
        native-lib

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

# 参数传入的是指定包含CMakeLists文件的目录的相对路径
add_subdirectory(one)  # 添加子目录one, 为手动添加
add_subdirectory(two)  # 添加子目录two, 为手动添加

静态注册:

因为Android Studio默认是静态注册,所以工程自动在native-lib.cpp中生成了如下代码:

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

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

工程自动在MainActivity.java中生成如下方法与C++方法连接:

public native String stringFromJNI();

C++代码有JNIEXPORT和JNICALL,这两个宏定义的作用就是说明该函数为JNI函数,在Java虚拟机加载的时候会链接对应的native方法。

JNI函数名的构成是:以Java为前缀,并且用“_”下划线将包名、类名以及native方法名连接起来就是对应的JNI函数了。

静态注册的JNI方法的弊端:

  • 名字过长,要遵循规则
  • 运行时去找效率不高

动态注册:

动态注册在jni层实现,JAVA层不需要关心方法的命名。JAVA在System.loadLibrary("native-lib");中自动调用JNI_OnLoad方法,在此方法中JNI层与JAVA层的方法进行关连。

动态注册的原理:JNI 允许我们提供一个函数映射表(一个结构体数组JNINativeMethod),注册给 JVM,这样 JVM 就可以用函数映射表来调用相应的函数。

动态注册过程

1.参数映射表

在JNI层添加函数映射表,Native方法都在这里添加。这是一个结构体数组,这个工程中因为只有一个Native方法,所以数组中只有一个成员。

static JNINativeMethod gMethods_MainActivity[] = {
        {"stringFromJNI", "()Ljava/lang/String;", (void*)strFromJni}
};

参数说明:

参数1:就是java代码中用native关键字声明的函数名字符串

参数2:native方法的签名(参数类型和返回值类型)

参数3:C/C++中对应函数的函数名(地址)

2.注册native方法

将native方法关联到JAVA层的类中。

// JAVA层的类
static const char* gClassName = "org/mu/myfirsthellonative/MainActivity";

static int registerNatives(JNIEnv *env, const char* classname) {
    jclass clazz;
    clazz = env->FindClass(classname);
    if(clazz == NULL) {
        return JNI_FALSE;
    }
    if(env->RegisterNatives(clazz, gMethods_MainActivity, sizeof(gMethods_MainActivity) / sizeof(gMethods_MainActivity[0])) < 0) {
        return JNI_FALSE;
    }
    return JNI_TRUE;
}
3.载入JNI方法

因为 JVM 执行到 System.loadLibrary() 函数时,会立即调用 JNI_OnLoad() 方法,因此在该方法中进行各种资源的初始化操作最为恰当。

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
//    jint result = -1;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    assert(env != NULL);
    //为了方便管理我们可以将native方法分别注册到java的不同类中
    //这里只有一个方法
    registerNatives(env, gClassName); //注册MainActivity类的native方法
    return JNI_VERSION_1_6;
}
4.实现的Native方法

此处定义了C++的方法,以供JAVA调用。因为JAVA层的方法是实例方法,此处参数为jobject类型。若JAVA层的方法是静态方法,此处参数为jclass类型。

jstring strFromJni(JNIEnv *env, jobject /*0bj*/) {
    std::string hello = "欢迎来到native的世界";
    return env->NewStringUTF(hello.c_str());
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值