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

2 篇文章 0 订阅

1、介绍JNI注册方式

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

静态注册的实现可以参考我的这篇博客https://blog.csdn.net/Tongseng/article/details/82633221

 

2、动态注册的步骤

先看有几个文件

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 {
    	public	JniClient() {
    	}
    	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 void
    Z jboolean boolean
    I jint int
    J jlong long
    D jdouble double
    F jfloat float
    B jbyte byte
    C jchar char
    S 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 jstring
Ljava/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文件

 

3、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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值