Android JNI总结
@Dlive
0x01 JNI介绍
JNI是Java Native Interface的缩写,JNI不是Android专有的东西,它是从Java继承而来,但是在Android中,JNI的作用和重要性大大增强。
JNI在Android中起着连接Java和C/C++层的作用,现在APP的许多重要的逻辑,算法以及和底层的交互功能都是通过JNI调用C/C++来实现。
简单来说,JNI提供了一种可以让Java代码调用C/C++代码的接口。
0x02 JNI中的类型/数据结构
JNI的类型/数据结构,以及函数的声明都放在jni.h的头文件中,这个头文件可以在NDK的platforms文件夹下android对应版本的对应架构文件夹下找到。比如
platforms\android-21\arch-arm64\usr\include\jni.h
一些比较重要的类型如下:
1. 基本类型(都是一些C类型的重命名)
2. 常见的对象类型
jobject 代表Java中的对象
jstring 代表Java中的String类型
jclass 代表Java中的类
jobjectArray/jbooleanArray 等都是数组类型
3. JNIEnv代表JNI环境的结构体
可以看到JNIEnv结构体中有大量函数指针,我们可以通过这个结构体来调用jni.h中声明的函数,之后会有代码来说明这结构体的作用
0x03 Native函数的静态注册与动态注册
前面的数据类型比较无聊,其实我们主要关注的还是Java native函数是怎么和C/C++关联起来的,关联起来后,C/C++函数的编写和正常的C/C++代码基本相同。
Native函数的注册有两种方式:静态注册和动态注册。
关于使用Android Studio编写Native代码的相关配置可以参考:
http://tools.android.com/tech-docs/new-build-system/gradle-experimental
1. 静态注册
静态注册较为简单,所以先来用一个例子来说明一下静态注册。
使用AndroidStdio新建项目,我们把项目命名为HelloNative
在项目中新建一个文件夹jni
在MainActiviy中声明两个native属性函数,第一个函数为static, AS提供alt+enter快捷键可以新建mainactivity并快速生成这两个函数对应的Native层函数框架
public static native String Hello1();
public native String Hello2(int vint);
在MainActivity开始处加载so库
static {
System.loadLibrary("mainactivity");
}
mainactivity.c:
#include <jni.h> JNIEXPORT jstring JNICALL
Java_dlive_hellonative_MainActivity_Hello1(JNIEnv *env, jclass type) {
return (*env)->NewStringUTF(env, "natvie hello1");
} JNIEXPORT jobject JNICALL
Java_dlive_hellonative_MainActivity_Hello2(JNIEnv *env, jobject instance, jint vint) { // TODO
return (*env)->NewStringUTF(env, "native hello2");
}
mainactiviy.c中的函数就是我们要编写的native函数
Java_dlive_hellonative_MainActivity_Hello1对应Java中声明的Hello1函数
Java_dlive_hellonative_MainActivity_Hello2对应Java中声明的Hello2函数
这就是静态注册,即Native函数的函数名由Hello1的packagename和Hello1的函数名组成,Java层调用Hello1时,会调用mainactivity.so中对应的
Java_dlive_hellonative_MainActivity_Hello1函数。
这里还有几点要注意:
1. Java层函数对应的Native层函数的第一个参数为JNIEnv*
2. 被声明为static的函数Hello1对应的Native函数
Java_dlive_hellonative_MainActivity_Hello1 第二个参数为jclass,表示Hello1函数所在的类
3. 没有static声明的函数对应的Native函数的第二个参数为jobject,表示调用Hello2的对象
4. Native函数从第三个参数开始才对应Java层函数的参数
2. 动态注册
动态注册与静态注册的区别在于Native的函数名可以自定义,然后使用JNI提供的
RegisterNatives动态将Java层函数和Native层函数绑定起来即可。
动态注册主要依赖两个函数JNI_OnLoad和RegisterNatives
JNI_Onload会在so加载的时候自动被调用,在JNI_Onload中调用RegisterNatives将Java层函数和Native层函数关联起来。
MainActivity.java中native属性的函数:
public native String Hello3(MainActivity main);
在jni目录下新建hello.cpp和hello.h,在hello.cpp中实现native函数
jstring helloNative(JNIEnv* env, jobject jobj, jobject jobj1)
{
return env->NewStringUTF("hello native 3");
}
可以看到实现的函数名称为helloNative,而不是静态注册时很长的函数名。
const char* gClassName = "dlive/hellonative2/MainActivity";
//JNINativeMethod是一个结构体
JNINativeMethod gMethods[] = {
{"Hello3", "(Ldlive/hellonative2/MainActivity;)Ljava/lang/String;", (void*)helloNative},
};
gClassName : Java层类名
gMethods:{java层函数名,java层函数签名【(参数)返回值】,native层函数指针}
//reserved 保留参数
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void* reserved) {
JNIEnv* env = NULL;
// jint result = -1;
if(vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
if (registerNativeMethods(env, gClassName, gMethods,
sizeof(gMethods) / sizeof(gMethods[0])) == JNI_FALSE)
{
return -1;
}
LOGD("So load success");
return JNI_VERSION_1_6;
}
registerNativeMethods是找了一个网上的实现,就是封装了一下RegisterNatives
int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod *gMethods, int numMethods) {
jclass clazz;
clazz = env->FindClass(className);
if(clazz == NULL) {
return JNI_FALSE;
}
if(env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
0x04 JNI中提供的函数
JNI中提供了哪些函数可以在jni.h中找到,这里只讲一下常用的几个函数。
讲解顺序是按照jni.h中声明的顺序排序的。
//根据packagename找到并返回java class //比如 jclass stringClass = env -> FindClass("java/lang/String"); //jobjectArray array = env -> NewObjectArray(count, stringClass, NULL); jclass FindClass(const char *name) |
//生成一个Java对象 //clazz为FindClass的返回结果 //methodID指Java类的构造函数ID //调用一个Java对象的方法或者存取一个Java对象的域变量前,要先知道这个方法或域变量的ID //取得方法ID和域ID的函数: //GetMethodID //GetFieldID //例子: //jclass clazz = env->FindClass("android/content/Intent") //jmethodID method = env->GetMethodID(clazz, "<init>", "Ljava/lang/String") //jstring action = env -> NewStringUTF("android.intent.action.MAIN") //jobject intent = env -> NewObject(clazz, method, action) jobject NewObject(jclass clazz, jmethodID methodID, ...) |
//clazz为FindClass返回值 //name为函数名字,如构造函数为 "<init>" //sig为函数签名, 如Content类<init>的签名为,"Ljava/lang/String" (参数) jmethodID GetMethodID(jclass clazz, const char *name, const char *sig) |
//获取字符串内容,对应ReleaseStringUTFChars,该函数返回的指针要使用ReleaseStringUTFChars释放 const char* GetStringUTFChars(jstring str, jboolean *isCopy) |
//创建clazz的对象数组,len为长度,init表示将数组元素初始化为什么一般为NULL //jobjectArray array = env -> NewObjectArray(count, stringClass, NULL);S jobjectArray NewObjectArray(jsize len, jclass clazz, jobject init) |
//获取对象数组里的元素,第二个参数为数组下标 //如果一次只取一个元素,可以使用下面的函数,也不用释放内存,更加方便,参考《深入解析Android5.0系统》 jobject GetObjectArrayElement(jobjectArray array, jsize index) |
//给对象数组元素赋值 // 例: // for(int i=0; i< count; i++) // { // jstring str = env -> NewStringUTF("HELLO"); // if(str == NULL) // { // return NULL; // } // env -> SetObjectArrayElement(array, i, str); // } // return array; void SetObjectArrayElement(jobjectArray array, jsize index,jobject val) |
//返回int数组指针,第二个参数值是否拷贝一份数组出来,数组用完后要使用ReleaseIntArrayElements释放数组内存 jint * GetIntArrayElements(jintArray array, jboolean *isCopy) |
Android JNI总结的更多相关文章
- [转载]—— Android JNI知识点
Java Native Interface (JNI)标准是java平台的一部分,它允许Java代码和其他语言写的代码进行交互.JNI 是本地编程接口,它使得在 Java 虚拟机 (VM) 内部运行的 ...
- Android JNI 和 NDK
1.Android NDK 一.NDK产生的背景 Android平台从诞生起,就已经支持C.C++开发.众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第 ...
- Android jni简便开发流程
<Android jni helloworld>中介绍了开发jni helloworld的步骤,本文将介绍jni简便开发流程 ① 写java代码 native 声明本地方法 ② 添加本地支 ...
- Android jni系统变量、函数、接口定义汇总
在做Android jni开发时,jni为我们提供了哪些函数.接口.变量,有时候一头雾水,今天就把jni.h中定义的所有内容列出来,供自己查阅: /* * Copyright (C) 2006 The ...
- android JNI调用(转)
Android jni开发资料--NDK环境搭建 android开发人员注意了 谷歌改良了ndk的开发流程,对于Windows环境下NDK的开发,如果使用的NDK是r7之前的版本,必须要安装Cygwi ...
- Android JNI如何调用第三方库
http://www.2cto.com/kf/201504/388764.html Android JNI找不到第三方库的解决方案 cannot load library 最近做一个jni项目,拿到的 ...
- Android JNI之JAVA与C++对象建立对称关联(JNI优化设计,确保JNI调用的稳定性)
转载请声明:原文转自:http://www.cnblogs.com/xiezie/p/5930503.html Android JNI之JAVA与C++对象建立对称关联 1.JAVA对象持有C++对象 ...
- 利用gdb 调试android jni c动态库
http://blog.dornea.nu/2015/07/01/debugging-android-native-shared-libraries/ Since I haven't done thi ...
- Android JNI使用方法
经过几天的努力终于搞定了android JNI部分,下面将我的这个小程序和大家分享一下.android JNI是连接android Java部分和C/C++部分的纽带,完整使用JNI需要Java代码和 ...
- 【转】android JNI
原文网址:http://jinguo.iteye.com/blog/696185 Java Native Interface (JNI)标准是java平台的一部分,它允许Java代码和其他语言写的代码 ...
随机推荐
- android-配置文件AndroidManifest.xml
AndroidManifest.xml 是每个android程序中必须的文件.它位于整个项目的根目录,描述了package中暴露的组件(activities, services, 等等),他们各自的实 ...
- wm_concat
select to_char(wm_concat(ssss)) from (select replace(C_CELL_CONTENT ,'=$','') ssss ,rownum ss from ( ...
- Java之类的构造器(反射)
反射: Java反射机制:指的是在Java程序运行状态中,对于任何一个类,都可以获得这个类的所有属性和方法;对于给定的一个对象,都能够调用它的任意一个属性和方法.这种动态获取类的内容以及动态调用对象的 ...
- Javascript JQuery获取当前元素的兄弟元素/上一个/下一个元素(转)
var chils= s.childNodes; //得到s的全部子节点 var par=s.parentNode; //得到s的父节点 var ns=s.nextSbiling; //获得s的下一个 ...
- 2.7我们的第一个Java程序
最后,让我们正式编一个程序(注释⑤).它能打印出与当前运行的系统有关的资料,并利用了来自Java标准库的System对象的多种方法.注意这里引入了一种额外的注释样式:“//”.它表示到本行结束前的所有 ...
- 写了个项目 Web-Rtmp: 使用 WebSocket 在网页上播放 RTMP 直播流
http://neue.v2ex.com/t/316766 虽说浏览器里用 js 解码'播放'视频的方案已经有几个了... 为什么不再多一个呢... 基本原理: 服务端使用 websockify 中转 ...
- Le lié à la légèreté semblait être et donc plus simple
Il est toutefois vraiment à partir www.runmasterfr.com/free-40-flyknit-2015-hommes-c-1_58_59.html de ...
- Nginx配置(全)
安装 * windows下:直接到官网下载windows下的稳定版本,解压到任意位置即可 * Linux下:一种方式是yum或者sudo apt-get install 另一种是源码编译安装,先安装p ...
- Swift—调用系统相册和相机
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 24.0px Menlo; color: #000000 } p.p2 { margin: 0.0px 0. ...
- mac显示隐藏文件
苹果Mac OS X操作系统下,隐藏文件是否显示有很多种设置方法,最简单的要算在Mac终端输入命令. 显示/隐藏Mac隐藏文件命令如下(注意其中的空格并且区分大小写): 第一种 显示Mac隐藏文件的命 ...