Android之JNI动态注册native方法和JNI数据简单使用
2017-05-07 23:56
603 查看
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 { 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文件,运行项目,就有上面图片的结果
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;
相关文章推荐
- JNI动态注册native方法及JNI数据使用
- JNI动态注册native方法及JNI数据使用
- JNI动态注册native方法及JNI数据使用
- 安卓实战开发之JNI从小白到伪老白深入了解JNI动态注册native方法及JNI数据使用
- Android JNI使用方法(“动态注册”)
- Android Studio Jni开发(三)Native方法动态注册
- Android JNI动态注册Native 方法(实现IDA中改名)
- Android JNI使用方法(“动态注册”)
- 安卓 jni 开发之 native 方法的动态注册
- android开发--- jni使用RegisterNatives注册本地方法
- Android JNI 动态注册方法(JNI_OnLoad)
- AndroidJNI实践(1)-使用.h头文件-静态注册JNI方法
- Android Jni 注册方法。以及在eclipse作为第三方so的使用
- Android JNI开发(1)--JavaVM和 JNIEnv 动态注册本地方法
- OJ常用动态数据输入方法 C++简单的栈编写及使用
- Android Studio NDK 入门教程(8)--JNI动态注册本地方法
- Android之JNI实例一--简单的Java调用Native方法
- Android使用JNI实现Java与C之间传递数据
- 在Android系统使用socket在Java层和native之间数据通信
- 使用简单的方法进行批量删除数据