JNI学习笔记_Java调用C —— Android中使用的方法
一、笔记
1.JNI(Java Native Interface),就是如何使用java去访问C/C++编写的那些库。
若想深入了解JNI可以看官方文档jni.pdf。
优秀博文:
Android JNI知识简介:http://blog.csdn.net/linweig/article/details/5417319
Android JNI(实现自己的JNI_OnLoad函数):http://jjf19850615.blog.163.com/blog/static/356881472013342153912 (这个看不了了)
2.JNI协议作用位置
Java Android是通过Java写的
---------------
JNI JNI是Java调用C/C++库的协议
---------------
C/C++ App、C库 可以直接系统调用
---------------
驱动 Linux
3.JNI的实现步骤:
(1) 加载C库
(2) 建立java函数与C函数的映射,有两种建立方法,分别为显式建立和隐式建立
(1)显式建立:使用(*env)->RegisterNatives()注册。
(2)隐式建立:如果java源文件中类a.b.c.d.JNIDemo要调用hello(),那么C源文件中要实现Java_a_b_c_d_JNIDemo_hello()这个函数,
函数名要严格对应。对应的C中的函数的函数名同样可以使用javah来生成。
使用隐式建立时C源文件中就不需要再定义JNI_OnLoad()了。
但是一般倾向于建立显式建立,因为比较灵活。
(3) 调用C函数
4.jni.h文件
可以通过它来看env中提供的函数的函数签名,其路径:/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/jni.h
注意:需要使用-I指定jni.h的路径名以便#include可以包含的到它。
5.JNI的字段描述符
用于描述C源文件中的JNINativeMethod.signature字段
①参数中的数组使用[表示,例如int[] a为[I
②对于类要用全称“L包.子包.类名;”(前面有L后面有;),eg:"Ljava/lang/String;"
除了String类之外的其它所有类都要使用“L/java/lang/Object”来表示。
对于类参数和返回值都是一样的表示。
也可以使用一个取巧的办法,使用javah生成一个JNIDemo.h头文件,这个头文件中就有这些描述符。
$ javac JNIDemo.java
$ javah -jni JNIDemo
6.JNI传递参数
(1) 基本类型的参数传递非常简单,java传给C文件的参数C文件中直接使用就可以了。对基本类型的参数传递是透明的。
(2) 参数或返回值是类名时对应C中的函数签名可以借助javah来查看
①对于字符串类的双向传递需要使用env中的一些函数。参考jni.pdf pg39,
②传递数组时C文件中的函数签名可以参考javah生成的头文件。参考jni.pdf pg48
7.java函数映射的C文件中的函数前面的修饰JNICALL是一个空的宏,写不写无所谓。
8.java文件怎样使用native函数总结
java文件中声明native方法,然后在类的静态代码块中调用System.loadLibrary("本地库")来加载本地库,这会触发本地库中的onLoad()被调用。
本地需要实现java类中的native方法,并在onLoad()中将这些方法注册到java类中。此后java就可以调用本地方法了。
二、Java调用C函数的例子
1.基本实现框架
/*file: JNIDemo. java*/ public class JNIDemo {
/*
* 一般来说会在静态代码块里来加载这个库。因为一
* 个静态代码块会在构造一个对象之前执行,并且只
* 执行一次。
* 它会导致C中实现的JNI_OnLoad()被调用。
*/
static { /* 1. load */
System.loadLibrary("native"); /* 加载libnative.so这个C库 */
}
/*
* 加native关键字修饰表示hello()是定义在C文件中的,
* 这里只是简单的修饰就可以调用了。注意:hello()前
* 没有加static,因此需要创建一个实例化对象调用hello()
*/
public native int hello(int a);
public static void main (String args[]) {
JNIDemo d = new JNIDemo(); /*创建JNIDemo类的对象d*/ /* 2. map java hello <-->c c_hello */ /*hello()是在java中实现的,c_hello()是在C中实现的,映射关系的建立是在C中建立的(显式映射)*/ /* 3. call */
System.out.println(d.hello());
}
} /* 非static 函数需要通过对象来调用,static可以直接调用*/
public class JNIDemo {
static { /* 1. load */
System.loadLibrary("native"); /* 加载libnative.so这个C库 */
}
public native static void hello(); /*加native关键字修饰表示hello()是定义在C文件中的,这里知识简单的修饰就可以调用了*/
public static void main (String args[]) {/* 2. map java hello <-->c c_hello */
/* 3. call */
hello(); /*与上一个不同,上面申明hello()时加了static修饰,所以这里可以直接调用*/
}
}
/*file: native.c*/ /*
* jni.h位于/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/目录下,
* --sysroot指定这个路径,因此编译C程序时需要指定:
* -I/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/
*/
#include <jni.h>
#include <stdio.h> #if 0
typedef struct {
char *name; /* 这个name就是在Java里调用的函数名,此列子为hello */
char *signature; /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */
void *fnPtr; /* C语言实现的本地函数的函数指针 */
} JNINativeMethod;
#endif
jint c_hello(JNIEnv *env, jobject obj, jint a) {
printf("Hello JNI, a=%d\n", a);
return 222;
}
/*
* 既然methods[]是个数组就说明可以同时注册多个本地函数
*/
static const JNINativeMethod methods[] = {
/*
* 本地定义的这个c_hello(除了env和cls)没有参数,
* 所以signature字段中‘()’里面为空,返回值是void
* 所以是"()V". 没有参数是指在java中调用这个函数没有参数。
* 函数签名的写法补充:
* 参数中的数组使用[表示,例如int[] a为[I
* 对于类要用全称“L包.子包.类名;”(前面有L后面有;),eg:"Ljava/lang/String;"
* 除了String类之外的其它所有类都要使用“L/java/lang/Object”来表示。
* 对于类参数和返回值都是一样的表示。
*/
{"hello", "(I)I", (void *)c_hello},
};
/* java程序中执行System.loadLibrary()加载这个C库的时候
* 就会先执行C库中定义的JNI_OnLoad(),因此需要在这个函数中
* 建立映射关系。
* 这个函数怎么写参考jni.pdf pg117
*/
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
JNIEnv *env;
jclass cls; /*
* 获取java程序的运行环境,因为之后需要通过env调用FindClass
* 等函数。
* JNI有1.1 1.2 1.4这几个版本,最后还要返回这个版本,1.1是最
* 老的。
*/
if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) { /*获取运行环境*/
return JNI_ERR; /* JNI version not supported */
} /*
* java中的函数是通过类来写的。C中的c_hello()要映射
* 到JNIDemo中声明的hello()上,所以要先查找这个类。
*/
cls = (*env)->FindClass(env, "JNIDemo");
if (cls == NULL) {
return JNI_ERR;
} /*
* 建立映射关系,cls是通过java程序获取到的,methods是
* C程序中的。
* methods数组中的数组项只有1项。
*/
/* 2. map java hello <-->c c_hello */
if ((*env)->RegisterNatives(env, cls, methods, ) < )
return JNI_ERR; return JNI_VERSION_1_4;
}
上面是显式映射的例子,下面是隐式映射:
/*file: native.c*/ #include <stdio.h>
#include <jni.h> JNIEXPORT jint JNICALL Java_JNIDemo_hello(JNIEnv *env, jobject obj, jint a) {
printf("Hello JNI, a=%d\n", a);
return ;
}
编译运行方法:
javac JNIDemo.java
gcc -I/usr/lib/jvm/java-1.7.-openjdk-amd64/include/ -fPIC -shared -o libnative.so native.c
export LD_LIBRARY_PATH=.
java JNIDemo 注意:
.编译成C库的操作中若没有加-fPIC会报错“Couldn't read symbols”.
.需要设置环境变量LD_LIBRARY_PATH,否则“no native in java.library.path”
2.双向传递字符串
JNIDemo.java中修改如下:
public class JNIDemo { static { // 1. load
System.loadLibrary("native");
} public native String hello(String str); //still is int in java, but jint in C public static void main(String args[]) {
JNIDemo d = new JNIDemo(); System.out.println(d.hello("Hello JNI String!"));
} }
native.c中修改如下:
jstring c_hello(JNIEnv *env, jobject cls, jstring str) {
const jbyte *cstr;
cstr = (*env)->GetStringUTFChars(env, str, NULL);
printf("Get string from java :%s\n", cstr);
(*env)->ReleaseStringUTFChars(env, str, cstr);
return (*env)->NewStringUTF(env, "return from c");
} static const JNINativeMethod methods[] = {
{"hello", "(Ljava/lang/String;)Ljava/lang/String;", (void *)c_hello},
};
3.双向传递数组
/*file: JNIDemo.java*/ public class JNIDemo { static { // 1. load
System.loadLibrary("native");
} public native int[] hello(int[] arr); public static void main(String args[]) {
JNIDemo d = new JNIDemo(); int[] arr = {1, 2, 3};
int[] b = null;
int i; b = d.hello(arr); for (i = 0; i < b.length; i++)
System.out.println(b[i]);
} }
/*file: native.c*/ #include <stdio.h>
#include <stdlib.h>
#include <jni.h> jintArray c_hello(JNIEnv *env, jobject cls, jintArray arr)
{
jint *carr;
jint *oarr;
jintArray rarr; jint i, n = ;
carr = (*env)->GetIntArrayElements(env, arr, NULL);
if (carr == NULL) {
return ; /* exception occurred */
} n = (*env)->GetArrayLength(env, arr);
oarr = malloc(sizeof(jint) * n);
if (oarr == NULL) {
(*env)->ReleaseIntArrayElements(env, arr, carr, );
return ;
} for (i = ; i < n; i++) {
oarr[i] = carr[n--i];
} (*env)->ReleaseIntArrayElements(env, arr, carr, ); /* create jintArray */
rarr = (*env)->NewIntArray(env, n);
if (rarr == NULL) {
return ;
} (*env)->SetIntArrayRegion(env, rarr, , n, oarr);
free(oarr); return rarr;
} static const JNINativeMethod methods[] = {
{"hello", "([I)[I", (void *)c_hello},
}; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
jclass cls; if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_4)) {
return JNI_ERR; /* JNI version not supported */
} cls = (*env)->FindClass(env, "JNIDemo");
if (cls == NULL) {
return JNI_ERR;
} /* 2. map java hello <-->c c_hello */
if ((*env)->RegisterNatives(env, cls, methods, ) < )
return JNI_ERR; return JNI_VERSION_1_4;
}
三、相关资源收集
1. WDS JNIDemo源码:$ git clone https://github.com/weidongshan/JNIDemo.git
四、补充
1.$ java -verbose:jni StaticMethodCal 可以看出JNI库加载的信息
2. 签名中使用的是具体的类名
// frameworks/base/core/jni/android_hardware_SensorManager.cpp static JNINativeMethod gSystemSensorManagerMethods[] = {
{"nativeClassInit", "()V", (void*)nativeClassInit },
/*L+包名+类名*/
{"nativeGetNextSensor", "(Landroid/hardware/Sensor;I)I", (void*)nativeGetNextSensor },
};
static JNINativeMethod gBaseEventQueueMethods[] = {
{"nativeInitBaseEventQueue",
/*类的内部类直接使用"$"连接*/
"(Landroid/hardware/SystemSensorManager$BaseEventQueue;Landroid/os/MessageQueue;[F)J",
(void*)nativeInitSensorEventQueue },
};
JNI学习笔记_Java调用C —— Android中使用的方法的更多相关文章
- JNI学习笔记_Java调用C —— 非Android中使用的方法
一.学习笔记 1.java源码中的JNI函数本机方法声明必须使用native修饰. 2.相对反编译 Java 的 class 字节码文件来说,反汇编.so动态库来分析程序的逻辑要复杂得多,为了应用的安 ...
- JNI学习笔记_C调用Java
一.笔记 1.C调用Java中的方法,参考jni.pdf pg97可以参考博文:http://blog.csdn.net/lhzjj/article/details/26470999步骤: a. 创建 ...
- Android菜鸟的成长笔记(14)—— Android中的状态保存探究(上)
原文:[置顶] Android菜鸟的成长笔记(14)—— Android中的状态保存探究(上) 我们在用手机的时候可能会发现,即使应用被放到后台再返回到前台数据依然保留(比如说我们正在玩游戏,突然电话 ...
- Android菜鸟的成长笔记(15)—— Android中的状态保存探究(下)
原文:Android菜鸟的成长笔记(15)-- Android中的状态保存探究(下) 在上一篇中我们简单了解关于Android中状态保存的过程和原理,这一篇中我们来看一下在系统配置改变的情况下保存数据 ...
- Typescript 学习笔记四:回忆ES5 中的类
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- Android学习记录(3)—Android中ContentProvider的基本原理学习总结
一.ContentProvider简介 当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据.虽然使用其他方法也可以对外共享数据 ...
- 【opencv学习笔记七】访问图像中的像素与图像亮度对比度调整
今天我们来看一下如何访问图像的像素,以及如何改变图像的亮度与对比度. 在之前我们先来看一下图像矩阵数据的排列方式.我们以一个简单的矩阵来说明: 对单通道图像排列如下: 对于双通道图像排列如下: 那么对 ...
- ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则
ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...
- Flutter学习笔记(30)--Android原生与Flutter混编
如需转载,请注明出处:Flutter学习笔记(30)--Android原生与Flutter混编 这篇文章旨在学习如何在现有的Android原生项目上集成Flutter,实现Android与Flutte ...
随机推荐
- English trip -- VC(情景课)1 C What's your name?(review)
Xu言: 今天,阴差阳错又上了一次 VC 1 C的课,不过这次是小班的形式.这次课的教室叫 toronto [təˈrɒntəʊ] to ron to (多伦多(加拿大城市)) - -0我还 ...
- LeetCode--171--Excel表列序号
问题描述: 给定一个Excel表格中的列名称,返回其相应的列序号. 例如, A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -& ...
- codeforces 497c//Distributing Parts// Codeforces Round #283(Div. 1)
题意:有n个区间[ai,bi],然后有n个人落在[ci,di],每个人能用ki次.问一种方式站满n个区间. 两种区间都用先x后y的升序排序.对于当前的区间[ai,bi],将ci值小于当前ai的全部放入 ...
- 新项目中使用的linux命令
要通过跳板机进入内网之后,访问内网域名 mysql -h xxxxxxx -u u_caojiangjiang -p -P 3306 上传文件: scp -r /Users/qudian/Deskto ...
- scrapy微信爬虫使用总结
scrapy+selenium+Chrome+微信公众号爬虫 概述 1.微信公众号爬虫思路: 参考:记一次微信公众号爬虫的经历 2.scrapy框架图 3.scrapy经典教程 参考: python ...
- LeetCode 318. Maximum Product of Word Lengths (状态压缩)
题目大意:给出一些字符串,找出两个不同的字符串之间长度之积的最大值,但要求这两个字符串之间不能拥有相同的字符.(字符只考虑小写字母). 题目分析:字符最多只有26个,因此每个字符串可以用一个二进制数来 ...
- CSS样式属性——背景
背景——包括背景颜色.背景图片,以及背景图片的控制 background-color:设置背景色(transparent表示透明的背景色) background-image:设置元素的背景图片 ba ...
- adb命令(笔记)
1.adb shell su 进入root管理员权限(前提是手机已root) 2.chmod 可以修改文件夹的权限: $ adb shell $ su # chmod -R 777 /data/ ...
- jsp jsp常用指令
jsp指令是为jsp引擎设计的,他们并不直接产生任何可见输出,而只是告诉引擎如何处理jsp页面中的其余部分. jsp中的指令 page指令 include指令 taglib指令 jsp指令的基本语法 ...
- 组数排序非sort
<!doctype html> <html> <head> <meta charset="utf-8"> <meta name ...