Android(安卓)JNI概述
JNI(Java Native Interface) Java本地接口, Java代码使用JNI调用外部的本地C/C++代码,同样,外部的C/C++ 代码可以调用Java代码。
NDK与JNI区别:
(1)NDK:NDK是Google开发的一套开发和编译工具集, 主要用于Android的JNI开发;
(2)JNI : JNI是一套编程接口, 用来实现Java代码与本地的C/C++代码进行交互。
一、JNI编程步骤:
(1)声明native方法:在Java代码中声明 native method()方法;
(2)实现JNI的C/C++方法:在JNI层实现Java中声明的native方法,并在JNI层,将C/C++代码编译成动态库;
(3)加载动态库: 在Java代码中的静态代码块中加载JNI编译后的动态共享库。
二、JNI开发方式比较
静态注册 | (1)书写不便,JNI层函数名特别长; (2)初次调用native函数时要根据函数名搜索对应的JNI层函数来建立关联关系,这样很影响效率。 |
动态注册 | (1)书写方便; (2)使用用函数映射表来调用相应的函数,效率较高。 |
三、动态注册
1、关键接口介绍
(1)System.loadLibrary()函数
Java程序通过System.loadLibrary加载完JNI动态库,完成对C++函数的调用。
static{System.loadLibrary("HelloJNI");}
(2)JNI_OnLoad()函数
JNI_OnLoad()函数在VM执行System.loadLibrary(xxx)函数时被调用,有两个重要的作用:
a、指定JNI版本:告诉VM该组件使用那一个JNI版本(若未提供JNI_OnLoad()函数,VM会默认该使用最老的JNI 1.1版),如果要使用新版本的JNI,例如JNI 1.4版,则必须由JNI_OnLoad()函数返回常量JNI_VERSION_1_4(该常量定义在jni.h中)来告知VM。
b、初始化设定,当VM执行到System.loadLibrary()函数时,会立即先呼叫JNI_OnLoad()方法,因此在该方法中进行各种资源的初始化操作最为恰当,而动态注册的工作就是在这里完成的。
(3)JNINativeMethod结构体
JNINativeMethod结构体建立Java与C++函数的联系,在jni.h中被定义,如下:
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
第一个变量name是Java中函数的名字;
第二个变量signature,用字符串是描述了函数的参数和返回值;
第三个变量fnPtr是函数指针,指向C++函数。
(4)RegisterNatives()与registerNativeMethods()
RegisterNatives()通过调用registerNativeMethods()和JNINativeMethod结构体,将c/c++中的方法映射到Java空间。
下面看关键代码,全部代码下载
static JNINativeMethod methods[] = {{"hello", "()Ljava/lang/String;", (void*) hello_jni },};static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods){ jclass clazz; clazz = env->FindClass(className); if (clazz == NULL) { fprintf(stderr, "Native registration unable to find class '%s'", className); return JNI_FALSE; } if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { fprintf(stderr, "RegisterNatives failed for '%s'", className); return JNI_FALSE; } return JNI_TRUE;}static const char* classPathName = "com/example/hellojni/MainActivity";static int registerNatives(JNIEnv* env){ if (!registerNativeMethods(env, classPathName, methods, sizeof(methods) / sizeof(methods[0]))) { return JNI_FALSE; } return JNI_TRUE;}typedef union { JNIEnv* env; void* venv;} UnionJNIEnvToVoid;jint JNI_OnLoad(JavaVM* vm, void* reserved){ UnionJNIEnvToVoid uenv; uenv.venv = NULL; jint result = -1; JNIEnv* env = NULL; printf("JNI_OnLoad"); if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) { fprintf(stderr, "GetEnv failed"); goto bail; } env = uenv.env; if (!registerNatives(env)) { fprintf(stderr, "registerNatives failed"); } result = JNI_VERSION_1_4;bail: return result;}
2、JNI类型映射表
Java 类型 | Native 类型 | 类型说明 | Field descriptor |
boolean | jboolean | C/C++8位整型 | Z |
byte | jbyte | C/C++带符号的8位整型 | B |
char | jchar | C/C++无符号的16位整型 | C |
short | jshort | C/C++带符号的16位整型 | S |
int | jint | C/C++带符号的32位整型 | I |
long | jlong | C/C++带符号的64位整型 | J |
float | jfloat | C/C++32位浮点型 | F |
double | jdouble | C/C++64位浮点型 | D |
Object | jobject | 任何Java对象,或者没有对应java类型的对象 | Ljava/lang/Object; |
Class | jclass | Class对象 | Ljava/lang/Object; |
String | jstring | 字符串对象 | Ljava/lang/String; |
Object[] | jobjectArray | 任何对象的数组 | [Ljava/lang/Object; |
boolean[] | jbooleanArray | 布尔型数组 | [Z |
byte[] | jbyteArray | 比特型数组 | [B |
char[] | jcharArray | 字符型数组 | [C |
short[] | jshortArray | 短整型数组 | [S |
int[] | jintArray | 整型数组 | [I |
long[] | jlongArray | 长整型数组 | [J |
float[] | jfloatArray | 浮点型数组 | [F |
double[] | jdoubleArray | 双浮点型数组 | [D |
Void | Void | 空 | V |
3、JNI调试
(1)添加头文件JNIlog.h,链接下载;
(2)在Android.mk 中加入:
LOCAL_LDLIBS := -llog
(3)调用
LOGD("%s:%u, static_hello", __func__, __LINE__);
4、静态与非静态原生函数的区别
其区别在于第二个参数随本地方法是静态还是非静态而有所不同:
(1)非静态本地方法的第二个参数是对对象的引用;
(2)静态本地方法的第二个参数是对其Java类的引用;
其余的参数对应通常Java方法的参数,参数类型需要根据一定规则进行映射。
更多相关文章
- C语言函数的递归(上)
- Android(安卓)实现全屏 去掉标题栏
- android去掉EditView的默认焦点问题
- Google Admob广告Android(安卓)、简单应用
- android DES 字符串加密后 解密乱码
- Android高手进阶教程(十五)---Android中万能的BaseAdapter(Spinn
- Android(安卓)开发中常见的Eclipse排版设置
- Android提高第十八篇之自定义PopupWindow实现的Menu(TabMenu)
- 2018-05-27