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;
- Android之JNI动态注册native方法和JNI数据简单使用
- JNI动态注册native方法及JNI数据使用
- 安卓实战开发之JNI从小白到伪老白深入了解JNI动态注册native方法及JNI数据使用
- 安卓 jni 开发之 native 方法的动态注册
- Android Studio Jni开发(三)Native方法动态注册
- Android JNI学习之动态注册native函数
- Android Studio3.0开发JNI流程------JNI静态注册和动态注册(多个类的native动态注册-经典篇)
- Android jni 静态注册 和动态注册
- JNI动态注册方法
- Android 动态注册JNI
- Android: JNI动态注册
- Android JNI动态注册
- Android 动态注册JNI
- Android JNI 动态注册方法(JNI_OnLoad)
- 静态注册JNI和动态注册JNI
- 静态注册JNI和动态注册JNI
- Android JNI 使用的数据结构JNINativeMethod详解 动态注册JNI
- Android JNI动态注册Native 方法(实现IDA中改名)
- 0507 #4 NYISTOJ ASCII码排序
- H5 面试中会遇到的技术问题
- 微信小程序官方横向滚动tab
- JQmobile实现的订餐webAPP
- CPU卡设计实例及程序设计(二十八)ESAM外部认证
- Android之JNI动态注册native方法和JNI数据简单使用
- 欢迎使用CSDN-markdown编辑器
- 多线程详解
- CPU卡设计实例及程序设计(二十九)卡82H外部认证,ESAM04H,05H,06H外部认证
- nginx字符串和redis字符串比较
- js中如何输出空格
- 初次使用虚拟机心得感受
- ORB-SLAM2
- 持续集成、持续交付、持续部署