Jni相关基础1-解释以及常见配置或语法

md文件相关操作语法参考
java之jvm的javap命令
获取class字节码相关、类信息、方法签名(javap s xxx.class)

本文包括内容

  • 配置或小知识点
    AndroidStudio中需要打印C中的日志

  • 配置或小知识点
    jni方法的各个部分所代表的的含义
    本地代码的注册

  • 多线程相关
    JNI多线程注意事项以及

配置或小知识点

在AndroidStudio中需要打印C中的日志:

android{
  defaultConfig {
    //新版中其实默认已经支持了。只需要使用android/log.h即可打印日志
    ndk {
        ldLibs = "log" //此配置是指添加ndk的日志支持.实际使用:liblog.so
    }
  }
}

JNI相关知识点解释

示例方法:

//实现Java_com_test01_Test_firstTest方法
JNIEXPORT void JNICALL Java_com_test01_Test_firstTest(JNIEnv *, jobject){
   Console::WriteLine(L"第一个Jni小程序");
}

1、jni方法的各个部分所代表的的含义

JNIEXPORT void JNICALL Java_com_test01_Test_firstTest (JNIEnv * env, jobject obj);
  • JNIEXPORT :在Jni编程中所有本地语言实现Jni接口的方法前面都有一个"JNIEXPORT",这个可以看做是Jni的一个标志,至今为止没发现它有什么特殊的用处。
  • void :这个学过编程的人都知道,当然是方法的返回值了。
  • JNICALL :这个可以理解为Jni 和Call两个部分,和起来的意思就是 Jni调用XXX(后面的XXX就是JAVA的方法名)。
  • Java_com_test01_Test_firstTest:这个就是被上一步中被调用的部分,也就是Java中的native 方法名,这里起名字的方式比较特别,是:包名+类名+方法名。
  • JNIEnv * env:这个env可以看做是Jni接口本身的一个对象,在上一篇中提到的jni.h头文件中存在着大量被封装好的函数,这些函数也是Jni编程中经常被使用到的,要想调用这些函数就需要使用JNIEnv这个对象。例如:env->GetObjectClass()。(详情请查看jni.h)
  • jobject obj:刚才在Test类的main方法中有这样一段代码:
    Test t=new Test();
    t.firstTest();
    这个jobject需要两种情况分析。上段代码中firstTest方法是一个非静态方法,在Java中要想调用它必须先实例化对象,然后再用对象调用它,那这个时候jobject就可以看做Java类的一个实例化对象,也就是obj就是t。如果firstTest是一个静态方法,那么在Java中,它不是属于一个对象的,而是属于一个类的,Java中用Test.firstTest()这样的方式来调用,这个时候jobject就可以看做是java类的本身,也就是obj就是Test.class。

1、本地方法注册

本地代码的注册方式有两种:

  1. 在执行本地方法前,在Java代码里使用语句System.loadLibrary("foo")加载本地方法所有的链接库;
  2. 在本地方法的实现里,需要用到其他链接库里的本地方法时,第一种方法就不适用了,比如,在本地方法里声明另一个本地方法void JNICALL g_impl(JNIEnv *env, jobject self);但它的实现是在另一个链接库里实现的,则需要使用下面的代码来作为这个方法的实现:
//这里某一个本地方法里的代码,省略了其他部分代码
 JNINativeMethod nm;
 nm.name = "g";//需要使用的其他链接库里的本地方法的名字
/* 方法的描述,如返回值,参数等 */
 nm.signature = "()V";
 nm.fnPtr = g_impl;//在这里的本地方法的声明的名字
//注册g_impl方法
 (*env)->RegisterNatives(env, cls, &nm, 1);

方法g_imlp的声明并不需要遵循JNI的命名规范,因为这只是调用时的方法指针,并不需要展开代码(调用这个方法时,实际调用的是另一个链接库里名为g的JNI方法),所有不需要使用JNIEXPORT,但需要遵循JNI的调用规范。

多线程之间ENV环境变量的约束:

在本地方法里写有关多线程的代码时,需要知道下面几个约束:

(注:JVM可以跨线程共享)

  1. 一个JNIEnv指针只在与它关联的线程里有效,也就是说,在线程间传递JNIEnv指针和在多线程环境里通过缓存来使用它是不允许和不安全的。JVM在同一个线程里多次调用同一个本地方法时传递的是同一个JNIEnv指针,但在不同的线程里调用同一个本地方法时传递的是不同的JNIEnv指针。
  2. 本地引用只在创建它的线程里有效,也就是说你不能在线程间传递本地引用。因为在多线程的环境里可能会使用到相同的引用,因此我们需要将本地引用转型为全局引用
    在任意线程中获取env环境对象:
JavaVM *jvm; /* already set */
f(){
    JNIEnv *env;
    //建立当前线程的连接。将环境变量变量关联到当前线程,也就是获取到当前线程
    (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
    ... /* use env */

    //使用完成之后一定要调用此方法释放jvm和当前线程的连接
    javaVM->DetachCurrentThread();
}

你可能感兴趣的:(Jni相关基础1-解释以及常见配置或语法)