美文网首页
Android JNI开发

Android JNI开发

作者: 好大的太阳哦 | 来源:发表于2020-04-03 15:51 被阅读0次

Android JNI开发

什么是JNI?有什么用?

JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)

JNI一般创建流程

方法一

  1. java代码中创建本地方法使用 native 关键字标识 如:
public native void stringFromJNI();
public native void stringFromJNI2();
public native String stringFromJNI3(String s);
  1. 在c中编写对应方法如:
extern "C"
JNIEXPORT void JNICALL
Java_com_boby_openslaudio_MainActivity_stringFromJNI1(JNIEnv *env, jobject thiz) { 
    using namespace std;
    cout << "Hello___________World";
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_boby_openslaudio_MainActivity_stringFromJNI2(JNIEnv *env, jobject thiz) { 
    return env->NewStringUTF("hello word !");
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_boby_openslaudio_MainActivity_stringFromJNI3(JNIEnv *env, jobject thiz, jstring s) {
    const  char *str=env->GetStringUTFChars(test,0);
    env->ReleaseStringUTFChars(test,str);
    return env->NewStringUTF("aaa");
}
  • extern "C" : 确保c++编译器在调用c代码中的函数时使用未经修改的名称
  • JNIEXPORT : 标识后面接着实际jni返回的值,对应java方法中的返回值 如 void 、 jstring 、jint 等
  • JNICALL :标识后面接jni调用的c方法,方法名规律:JAVA_包名_类名_方法名
  • JNIEnv * env:这个env可以看做是Jni接口本身的一个对象,jni.h头文件中存在着大量被封装好的函数,这些函数也是Jni编程中经常被使用到的,要想调用这些函数就需要使用JNIEnv这个对象。例如:env->GetObjectClass()。(详情请查看jni.h)
  • jobject obj:代表着native方法的调用者本身,如: new NativeDemo();但如果native是静态的,那就是NativeDemo.class
  • 如果java中需要传参给c,前两个参数固定为 JNIEnv * env ,jobject obj 第三个参数开始则为传递的参数。

JVM将JNI接口指针传递给本地方法,本地方法只能在当前线程中访问该接口指针,不能将接口指针传递给其它线程使用。在VM中 JNI接口指针指向的区域用来分配和存储线程本地数据。

当Java代码调用本地方法时,VM将JNI接口指针作为参数传递给本地方法,当同一个Java线程调用本地方法时VM保证传递给本地方法的参数是相同的。不过,不同的Java线程调用本地方法时,本地方法接收到的JNI接口指针是不同的。

基本类型

Java Language Type JNI Type
boolean jboolean
byte jbyte
char jchar
short jshort
int jint
long jlong
float jfloat
double jdouble
All Reference type jobject

方法二

在 JNI_OnLoad中调用

registerNative 手动映射java方法

typedef struct{
    const char* name; //java层定义的名字
    const char* signature;//方法签名,有规格的字符串,标注输入和输出参数
    void* fnPtr;//真正需要映射到c的具体的Api
}JNINativeMethod;

注册Native的最佳时期

  • jint JNI_OnLoad(JavaVM *vm, void *reserved )
  • Jint JNI_OnUnload(JavaVM *vm, void *reserved)
  1. java层
public native String  _test();
  1. c++层
#define JNI_CLASS_PATH "com/boby/openslaudio/MainActivity"

extern "C"
JNIEXPORT void JNICALL
my_test_register(JNIEnv *env, jobject thiz) { 
   retrun env->NewStringUTF("this is a test of register")
}
//方法映射表
static JNINativeMethod g_methods[] ={
        "_test","()Ljava/lang/String;",(void*)my_test_register
};

jint JNI_OnLoad(JavaVM *vm,void *reserved){//同一个进程只有一个JavaVM
     JNIEnv *env = NULL;
    vm->GetEnv((void ** )&env,JNI_VERSION_1_6);
     jclass clazz = env->FindClass(JNI_CLASS_PATH);
        //注册本地方法 参数:class,方法映射表,方法映射表中的个数
    env->RegisterNatives(clazz,g_methods, sizeof(g_methods)/ sizeof(g_methods[0]));
    return JNI_VERSION_1_6;
}

JNI中的Signature

java与c/c++相互调用时,用于描述函数参数的描述符

可以简单理解为java 虚拟机中有一个映射表。函数名和参数拼接在一起形成一个唯一的key ,通过key可以找到函数指针

  • 输入参数放在( )内,输出参数放在( )外的右边
  • 多个参数之间顺序存放,用;分割

原始类型的Signature

java类型 符号
boolean Z
byte B
char C
short S
int I
long L
float F
double D
void V

类的Signature

  • java对象 “L包路径/类名” 如:Ljava/lang/String

  • java数组 “ [ ”

    例:

  • ([LStudent;])[LStudenty; --> Student[] Xxx(student[] s)

    • ([Ljava/lang/String;)[Ljava/lang/Object; --> Object[] Xxx(String[] s)
    • ([I;[Ljava/lang/String;LStudent;)[Ljava/lang/Object --> Object[] Xxx(int [] a,String[] b,Student c)

C/C++ 调Java方法

调用过程有点像java的反射

  1. FindClass 可以在c后c++中找到java中的类
  2. GetMethodID/GetFieldID 拿到方法和属性的索引值
  3. NewObject 获取对象
  4. Call<TYPE>Method/[G/S]et<Type>Field 调用方法或调用属性

java

public class Student {
    private String name;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
  //测试java调用
  public native String test_c_callJava();

extern "C"
JNIEXPORT jstring JNICALL
test_c_calljava(JNIEnv *env, jobject thiz) {
    //1.找到class
    jclass clazz=env->FindClass("com/boby/openslaudio/Student");
    // 2.找到属性/方法的索引值
    jmethodID  jmethod_init_id =env->GetMethodID(clazz,"<init>","()V");
    jmethodID  jmethod_set_id =env->GetMethodID(clazz,"setName","(Ljava/lang/String;)V");
    jmethodID  jmethod_get_id =env->GetMethodID(clazz,"getName","()Ljava/lang/String;");
    // 3.创建对象
    jobject  obj=env->NewObject(clazz,jmethod_init_id);
    //4.调用对应的方法
    env->CallVoidMethod(obj,jmethod_set_id,env->NewStringUTF("boby"));
    jstring name= (jstring)env->CallObjectMethod(obj, jmethod_get_id);
    return name ;
}

相关文章

网友评论

      本文标题:Android JNI开发

      本文链接:https://www.haomeiwen.com/subject/krzhphtx.html