Android之JNI动态注册native方法和JNI数据简单使用

来源:互联网 发布:iphone7plus实用软件 编辑:程序博客网 时间:2024/05/04 14:37

1、爆结果照片




2、介绍JNI注册方式

JVM 查找 native 方法有两种方式:
    1)、按照 JNI 规范的命名规则(静态注册)
   2) 、调用 JNI 提供的 RegisterNatives 函数,将本地函数注册到 JVM 中(动态注册)

静态注册的实现可以参考我的这篇博客 http://blog.csdn.net/u011068702/article/details/53070776   (ubuntu上最使用jni最简单易懂的例子)




3、动态注册的步骤

先看有几个文件

1、MainActivity.java

package com.example.chenyu.test;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.widget.TextView;public class MainActivity extends AppCompatActivity {    public static final String TAG = "TestJni";    public TextView mTv;    public JniClient mJniClient;    static {        System.loadLibrary("FirstJni");    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mTv = (TextView) findViewById(R.id.text);        mJniClient = new JniClient();        String result = mJniClient.getStr();        int sum = mJniClient.addInt(2, 3);        Log.d(TAG, " mTv.setText before");        mTv.setText("string is" + result + " and 2 + 3 is " + sum);        Log.d(TAG, " mTv.setText after");    }}

2、JniClient.java文件

package com.example.chenyu.test;public class JniClient {publicJniClient() {}public native String getStr();public native int addInt(int a, int b);}

3、JniClient.c文件(新建一个文件夹jni,然后把这个文件放在jni文件夹里面)

//// Created by chenyu on 5/7/17.//#include <stdlib.h>#include <string.h>#include <stdio.h>#include <jni.h>#include <assert.h>#define JNIREG_CLASS "com/example/chenyu/test/JniClient"//指定要注册的类jstring get_strstr(JNIEnv* env, jobject thiz){    return (*env)->NewStringUTF(env, "I am chenyu, 动态注册JNI");}jint add_int(JNIEnv* env, jobject jobj, jint num1, jint num2){    return num1 + num2;}/*** 方法对应表*/static JNINativeMethod gMethods[] = {        {"getStr", "()Ljava/lang/String;", (void*)get_str},        {"addInt", "(II)I", (void*)add_int},};/** 为某一个类注册本地方法*/static int registerNativeMethods(JNIEnv* env        , const char* className        , JNINativeMethod* gMethods, int numMethods) {    jclass clazz;    clazz = (*env)->FindClass(env, className);    if (clazz == NULL) {        return JNI_FALSE;    }    if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {        return JNI_FALSE;    }    return JNI_TRUE;}/** 为所有类注册本地方法*/static int registerNatives(JNIEnv* env) {    return registerNativeMethods(env, JNIREG_CLASS, gMethods,                                 sizeof(gMethods) / sizeof(gMethods[0]));}/** System.loadLibrary("lib")时调用* 如果成功返回JNI版本, 失败返回-1*/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 -1;    }    assert(env != NULL);    if (!registerNatives(env)) {//注册        return -1;    }    //成功    result = JNI_VERSION_1_4;    return result;}

我们先说明JniClient.c文件里面内容

1)、JNIEXPORT和JNICALL含义

我们先看jni.h文件,里面包含了头文件 jni_md.h文件

 

我们再来看jni_md.h文件

所以JNIEXPORT 和 JNICALL 是一个空定义

在静态注册native函数里面出现了下面这个函数http://blog.csdn.net/u011068702/article/details/53070776   (ubuntu上最使用jni最简单易懂的例子)

JNIEXPORT jint JNICALL Java_com_example_firstjni_JniClient_AddInt    (JNIEnv *, jclass, jint, jint); 
第一个参数:JNIEnv* 是定义任意 native 函数的第一个参数(包括调用 JNI 的 RegisterNatives 函数注册的函数),指向 JVM 函数表的指针,函数表中的每一个入口指向一个 JNI 函数,每个函数用于访问 JVM 中特定的数据结构。
第二个参数:调用 Java 中 native 方法的实例或 Class 对象,如果这个 native 方法是实例方法,则该参数是 jobject,如果是静态方法,则是 jclass。
第三个参数:Java 对应 JNI 中的数据类型,Java 中 int 类型对应 JNI 的 jint 类型。(后面会详细介绍 JAVA 与 JNI 数据类型的映射关系)

函数返回值类型:夹在 JNIEXPORT 和 JNICALL 宏中间的 jint,表示函数的返回值类型,对应 Java 的int 类型


我们用RegisterNatives动态获取本地方法

我们先看JNINativeMethod 结构体的官方定义

typedef struct {    const char* name;    const char* signature;    void* fnPtr;  } JNINativeMethod;
第一个变量name是Java中函数的名字。
第二个变量signature,用字符串是描述了Java中函数的参数和返回值
第三个变量fnPtr是函数指针,指向native函数。前面都要接 (void *)

所以JniClient.c文件里面有下面的代码

/*** 方法对应表*/static JNINativeMethod gMethods[] = {        {"getStr", "()Ljava/lang/String;", (void*)get_str},        {"addInt", "(II)I", (void*)add_int},};

第一个参数就是我们写的方法,第三个就是.h文件里面的方法,主要是第二个参数比较复杂.括号里面表示参数的类型,括号后面表示返回值。
“()” 中的字符表示参数,后面的则代表返回值。例如”()V” 就表示void * Fun();
“(II)V” 表示 void Fun(int a, int b);
“(II)I” 表示 int addInt(int a, int b);
"()Ljava/lang/String;" 表示String getStr();

这些字符与函数的参数类型的映射表如下:
字符 J类型 java类型


V void voidZ jboolean booleanI jint intJ jlong longD jdouble doubleF jfloat floatB jbyte byteC jchar charS jshort short

数组则以”[“开始,用两个字符表示
[I jintArray int[][F jfloatArray float[][B jbyteArray byte[][C jcharArray char[][S jshortArray short[][D jdoubleArray double[][J jlongArray long[][Z jbooleanArray boolean[]

如图:

对象类型:以”L”开头,以”;”结尾,中间是用”/” 隔开。如上表第1个
数组类型:以”[“开始。如上表第2个(n维数组的话,则是前面多少个”[“而已,如”[[[D”表示“double[][][]”)

如果Java函数的参数是class,则以”L”开头,以”;”结尾中间是用”/” 隔开的包及类名。而其对应的C函数名的参数则为jobject. 一个例外是String类,其对应的类为jstring

Ljava/lang/String; String jstringLjava/net/Socket; Socket jobject 

如果JAVA函数位于一个嵌入类,则用作为类名间的分隔符。例如“(Ljava/lang/String;Landroid/os/FileUtilsFileStatus;)Z”


重写JNI_OnLoad()方法这样就会当调用 System.loadLibrary(“XXXX”)方法的时候直接来调用JNI_OnLoad(),这样就达到了动态注册实现native方法的作用。

/** System.loadLibrary("lib")时调用* 如果成功返回JNI版本, 失败返回-1*/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 -1;    }    assert(env != NULL);    if (!registerNatives(env)) {//注册        return -1;    }    //成功    result = JNI_VERSION_1_4;    return result;}
为类注册本地方法

/** 为所有类注册本地方法*/static int registerNatives(JNIEnv* env) {    return registerNativeMethods(env, JNIREG_CLASS, gMethods,sizeof(gMethods) / sizeof(gMethods[0]));}

我们运行代码的时候要记得在build.gradle文件加上部分生成so文件的代码

defaultConfig {        applicationId "com.example.chenyu.test"        minSdkVersion 15        targetSdkVersion 23        versionCode 1        versionName "1.0"        ndk{            moduleName "FirstJni"            ldLibs "log", "z", "m"            abiFilters "armeabi", "armeabi-v7a", "x86"            //用于指定应用应该使用哪个标准库,此处添加c++库支持            stl "stlport_static"        //  支持stl            cFlags "-fexceptions"        // 支持exception        }        sourceSets.main{            jniLibs.srcDirs = ['libs']        }    }

然后我们进入这个项目的jni目录,然后运行命令

ndk-build

然后就会在libs文件夹下面的armeabi文件夹下面生成libFirstJni.so文件,运行项目,就有上面图片的结果




4、JNI数据类型及常用方法

基本类型和本地等效类型表:

接口函数表:

const struct JNINativeInterface ... = {    NULL,    NULL,    NULL,    NULL,    GetVersion,    DefineClass,    FindClass,    NULL,    NULL,    NULL,    GetSuperclass,    IsAssignableFrom,    NULL,    Throw,    ThrowNew,    ExceptionOccurred,    ExceptionDescribe,    ExceptionClear,    FatalError,    NULL,    NULL,    NewGlobalRef,    DeleteGlobalRef,    DeleteLocalRef,    IsSameObject,    NULL,    NULL,    AllocObject,    NewObject,    NewObjectV,    NewObjectA,    GetObjectClass,    IsInstanceOf,    GetMethodID,    CallObjectMethod,    CallObjectMethodV,    CallObjectMethodA,    CallBooleanMethod,    CallBooleanMethodV,    CallBooleanMethodA,    CallByteMethod,    CallByteMethodV,    CallByteMethodA,    CallCharMethod,    CallCharMethodV,    CallCharMethodA,    CallShortMethod,    CallShortMethodV,    CallShortMethodA,    CallIntMethod,    CallIntMethodV,    CallIntMethodA,    CallLongMethod,    CallLongMethodV,    CallLongMethodA,    CallFloatMethod,    CallFloatMethodV,    CallFloatMethodA,    CallDoubleMethod,    CallDoubleMethodV,    CallDoubleMethodA,    CallVoidMethod,    CallVoidMethodV,    CallVoidMethodA,    CallNonvirtualObjectMethod,    CallNonvirtualObjectMethodV,    CallNonvirtualObjectMethodA,    CallNonvirtualBooleanMethod,    CallNonvirtualBooleanMethodV,    CallNonvirtualBooleanMethodA,    CallNonvirtualByteMethod,    CallNonvirtualByteMethodV,    CallNonvirtualByteMethodA,    CallNonvirtualCharMethod,    CallNonvirtualCharMethodV,    CallNonvirtualCharMethodA,    CallNonvirtualShortMethod,    CallNonvirtualShortMethodV,    CallNonvirtualShortMethodA,    CallNonvirtualIntMethod,    CallNonvirtualIntMethodV,    CallNonvirtualIntMethodA,    CallNonvirtualLongMethod,    CallNonvirtualLongMethodV,    CallNonvirtualLongMethodA,    CallNonvirtualFloatMethod,    CallNonvirtualFloatMethodV,    CallNonvirtualFloatMethodA,    CallNonvirtualDoubleMethod,    CallNonvirtualDoubleMethodV,    CallNonvirtualDoubleMethodA,    CallNonvirtualVoidMethod,    CallNonvirtualVoidMethodV,    CallNonvirtualVoidMethodA,    GetFieldID,    GetObjectField,    GetBooleanField,    GetByteField,    GetCharField,    GetShortField,    GetIntField,    GetLongField,    GetFloatField,    GetDoubleField,    SetObjectField,    SetBooleanField,    SetByteField,    SetCharField,    SetShortField,    SetIntField,    SetLongField,    SetFloatField,    SetDoubleField,    GetStaticMethodID,    CallStaticObjectMethod,    CallStaticObjectMethodV,    CallStaticObjectMethodA,    CallStaticBooleanMethod,    CallStaticBooleanMethodV,    CallStaticBooleanMethodA,    CallStaticByteMethod,    CallStaticByteMethodV,    CallStaticByteMethodA,    CallStaticCharMethod,    CallStaticCharMethodV,    CallStaticCharMethodA,    CallStaticShortMethod,    CallStaticShortMethodV,    CallStaticShortMethodA,    CallStaticIntMethod,    CallStaticIntMethodV,    CallStaticIntMethodA,    CallStaticLongMethod,    CallStaticLongMethodV,    CallStaticLongMethodA,    CallStaticFloatMethod,    CallStaticFloatMethodV,    CallStaticFloatMethodA,    CallStaticDoubleMethod,    CallStaticDoubleMethodV,    CallStaticDoubleMethodA,    CallStaticVoidMethod,    CallStaticVoidMethodV,    CallStaticVoidMethodA,    GetStaticFieldID,    GetStaticObjectField,    GetStaticBooleanField,    GetStaticByteField,    GetStaticCharField,    GetStaticShortField,    GetStaticIntField,    GetStaticLongField,    GetStaticFloatField,    GetStaticDoubleField,    SetStaticObjectField,    SetStaticBooleanField,    SetStaticByteField,    SetStaticCharField,    SetStaticShortField,    SetStaticIntField,    SetStaticLongField,    SetStaticFloatField,    SetStaticDoubleField,    NewString,    GetStringLength,    GetStringChars,    ReleaseStringChars,    NewStringUTF,    GetStringUTFLength,    GetStringUTFChars,    ReleaseStringUTFChars,    GetArrayLength,    NewObjectArray,    GetObjectArrayElement,    SetObjectArrayElement,    NewBooleanArray,    NewByteArray,    NewCharArray,    NewShortArray,    NewIntArray,    NewLongArray,    NewFloatArray,    NewDoubleArray,    GetBooleanArrayElements,    GetByteArrayElements,    GetCharArrayElements,    GetShortArrayElements,    GetIntArrayElements,    GetLongArrayElements,    GetFloatArrayElements,    GetDoubleArrayElements,    ReleaseBooleanArrayElements,    ReleaseByteArrayElements,    ReleaseCharArrayElements,    ReleaseShortArrayElements,    ReleaseIntArrayElements,    ReleaseLongArrayElements,    ReleaseFloatArrayElements,    ReleaseDoubleArrayElements,    GetBooleanArrayRegion,    GetByteArrayRegion,    GetCharArrayRegion,    GetShortArrayRegion,    GetIntArrayRegion,    GetLongArrayRegion,    GetFloatArrayRegion,    GetDoubleArrayRegion,    SetBooleanArrayRegion,    SetByteArrayRegion,    SetCharArrayRegion,    SetShortArrayRegion,    SetIntArrayRegion,    SetLongArrayRegion,    SetFloatArrayRegion,    SetDoubleArrayRegion,    RegisterNatives,    UnregisterNatives,    MonitorEnter,    MonitorExit,    GetJavaVM,};
JNI与C/C++数据类型的转换(效率开发)

字符数组与jbyteArray


jbyteArray转字符数组

int byteSize = (int) env->GetArrayLength(jbyteArrayData);  //jbyteArrayData是jbyteArray类型的数据unsigned char* data = new unsigned char[byteSize + 1];env->GetByteArrayRegion(jbyteArrayData, 0, byteSize, reinterpret_cast<jbyte*>(data));data[byteSize] = '\0';


字符数组转jbyteArray
jbyte *jb =  (jbyte*) data;   //data是字符数组类型jbyteArray jarray = env->NewByteArray(byteSize);   //byteSize是字符数组大小env->SetByteArrayRegion(jarray, 0, byteSize, jb);



字符数组与jstring

    jstring转字符数组

char* JstringToChar(JNIEnv* env, jstring jstr) {    if(jstr == NULL) {        return NULL;    }    char* rtn = NULL;    jclass clsstring = env->FindClass("java/lang/String");    jstring strencode = env->NewStringUTF("utf-8");    jmethodID mid = env->GetMethodID(clsstring, "getBytes",            "(Ljava/lang/String;)[B");    jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);    jsize alen = env->GetArrayLength(barr);    jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);    if (alen > 0) {        rtn = (char*) malloc(alen + 1);        memcpy(rtn, ba, alen);        rtn[alen] = 0;    }    env->ReleaseByteArrayElements(barr, ba, 0);    return rtn;}



字符数组转jstring

jstring StrtoJstring(JNIEnv* env, const char* pat){    jclass strClass = env->FindClass("java/lang/String");    jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");    jbyteArray bytes = env->NewByteArray(strlen(pat));    env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);    jstring encoding = env->NewStringUTF("utf-8");    return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);}

最简单的可以直接使用
jstring jstr = env->NewStringUTF(str);

jint与int的互转都可以直接使用强转,如:
jint i = (jint) 1024;



0 0
原创粉丝点击