Android studio NDK 开发:

1)通过在Android project中t添加cpp 目录,在其中添加c 或 c++代码,在project build时,代码就被编译成本地库.so文件形式,Java层就可以通过JNI调用c /c++方法

2)具体配置:

  • 首先在main目录下添加directory :cpp/,
  • 编写c/c++代码(改代码需遵循一定规范,c++代码需要被包括在extern "C"{}中;不清楚如何编写该处代码,可以先在需要调用出添加native方法声明,例如:private native String getStringFromNative();然后打开Android studio的terminal,输入:javah -jni 包名+类名(添加native方法的类);执行完后会生成一个包名+类名的.h文件,然后就可以将.h文件中的方法声明复制到.cpp或.c文件中,具体编写方法实现)
  • 配置gradle(在app目录下新建CMakeLists.txt文件,鼠标选中.cpp/.c文件,点击link c/c++ project to gradle ;完成后会在build.gradle中添加exeternNativeBuild{}项;此外,在default config中还可以配置ndk{}项,里面添加需要生成支持哪些平台的.so文件,默认为全部支持。)
  • 编写CMakeLists.txt脚本(主要是添加addLibrary(hello [指定生成的库的名称] SHARED[表示生成共享库即.so文件] .c/.cpp文件路径))
  • 点击Build -> Rebuild Project(在工程的build/intermidiates/cmake/debug/obj下能找到各个平台的.so文件)

Android动态注册jni:

原理:直接告诉native函数其在JNI中对应函数的指针;JNI 允许我们提供一个函数映射表,注册给 JVM,这样 JVM 就可以用函数映射表来调用相应的函数,而不必通过函数名来查找相关函数(这个查找效率很低,函数名超级长)。

1.Java层调用system.loadlibrary()加载共享库后,会在该库中查找是否包含JNI_OnLoad()函数,有的话就会调用该函数,native方法的动态注册就是在该方法中完成的。

2.JNI_OnLoad()使用结构体JNINativeMethod保存Java Native函数和JNI函数的对应关系;

typedef struct {

const char* name; //Java中函数的名字

const char* signature;//符号签名,描述了函数的参数和返回值

void* fnPtr;//函数指针,指向一个被调用的函数

} JNINativeMethod;
//函数映射表
static JNINativeMethod methods[] = {
        {"stringFromJNI", "()Ljava/lang/String;", (void *) abcdefghijklmn},
        //这里可以有很多其他映射函数
};

3.使用RegisterNatives()函数完成注册

jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,jint nMethods)

完整样例:

#include <string.h>
#include <jni.h>
JNIEXPORT jstring JNICALL abcdefghijklmn( JNIEnv* env,jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;
    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return JNI_ERR;
    }
    JNINativeMethod gMethods[] = {
        { "stringFromJNI", "()Ljava/lang/String;", (void*)abcdefghijklmn },
    };
    jclass clazz = (*env)->FindClass(env, "com/example/hellojni/HelloJni");

    if (clazz == NULL) {
        return JNI_ERR;
    }
    if ((*env)->RegisterNatives(env, clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0){
        return JNI_ERR;
    }
    /* success -- return valid version number */
    result = JNI_VERSION_1_4;
    return result;
}

jni基础知识:http://cfanr.cn/2017/07/29/Android-NDK-dev-JNI-s-foundation/

JNIEnv JObject JClass解惑:

JNI语法区分c和c++环境;

JNIEnv本质是指向函数表指针的指针;

JNIEnv 特点:

JNIEnv 是一个指针,指向一组 JNI 函数,通过这些函数可以实现 Java 层和 JNI 层的交互,就是说通过 JNIEnv 调用 JNI 函数可以访问 Java 虚拟机,操作 Java 对象;

所有本地函数都会接收 JNIEnv 作为第一个参数;(不过 C++ 的JNI 函数已经对 JNIEnv 参数进行了封装,不用写在函数参数上)

用作线程局部存储,不能在线程间共享一个 JNIEnv 变量,也就是说 JNIEnv 只在创建它的线程有效,不能跨线程传递;相同的 Java 线程调用本地方法,所使用的 JNIEnv 是相同的,一个 native 方法不能被不同的 Java 线程调用;

C 语言的 JNIEnv:

由上面代码可知,C 语言的JNIEnv 就是const struct JNINativeInterface*,而 JNIEnv* env就等价于JNINativeInterface** env,env 实际是一个二级指针,所以想要得到 JNINativeInterface 结构体中定义的函数指针,就需要先获取 JNINativeInterface 的一级指针对象env,然后才能通过一级指针对象调用 JNI 函数,例如:

(*env)->NewStringUTF(env, “hello”)

C++的 JNIEnv:

由typedef _JNIEnv JNIEnv;可知,C++的 JNIEnv 是 _JNIEnv 结构体,而 _JNIEnv 结构体定义了 JNINativeInterface 的结构体指针,内部定义的函数实际上是调用 JNINativeInterface 的函数,所以C++的 env 是一级指针,调用时不需要加 env 作为函数的参数,例如:env->NewStringUTF(env, "hello")

参数:jobject obj的解释:

如果native方法不是static的话,这个obj就代表这个native方法的类实例

如果native方法是static的话,这个obj就代表这个native方法的类的class对象实例(static方法不需要类实例的,所以就代表这个类的class对象)

参考链接:JNI基础知识,http://blog.csdn.net/jiangwei0910410003/article/details/17465457

results matching ""

    No results matching ""