什么是JNI
JNI(Java Native Interface) java本地开发接口
JNI 是一个协议, 有了这个协议可以使Java代码和C/C++代码相互调用.
为什么用JNI
1、扩展JVM功能,访问驱动,操作硬件(物联网、智能家居、
车载电脑)
2、效率上c/c++语言效率更高数学运算,实时渲染的游戏上,音
视频处理
3、 java反编译比c语言容易, 通过JNI增强代码安全性(加密算法放到C实现)
4、代码移植,复用已经存在的c代码(opencv,ffmpeg…)
怎么用JNI
1.C/C++语言
2.掌握java jni流程
3.NDK (native develop kits )
开发环境介绍
windows下用轻量级 dev-c++
C语言基本数据类型
Java 八大基本数据类型 C基本数据类型
boolean
byte
char char
int int
float float
double double
long long
short short
C没有boolean byte
signed unsigned 有符号无符号修饰符只能修饰整形变量
signed 有符号最高位符号位可以表示负数
unsigned 无符号最高位仍然是数值位不可以表示负数
输入输出函数
输出函数
printf(“要输出的内容+占位符”,…….)
常用占位符
%d - int
%ld – long int
%lld - long long
%hd – 短整型
%c - char
%f - float
%lf – double
%u – 无符号数
%x – 十六进制输出 int 或者long int 或者short int
%o - 八进制输出
%s – 字符串
输入函数 Scanf
Int len;
Scanf(“%d”,&len); &取地址符号
什么是指针
int main(void) { int * p; //p 是变量的名字, int * 是一个类型 //这个变量存放的是int类型变量的地址。 int i =3; p=&i; system(“pause”); return 0; }
指针常见错误
指针变量声明后未赋值直接用
声明的指针类型和指向的类型要一致
指针和指针变量的关系
指针就是地址,地址就是指针
地址就是内存单元的编号
指针变量是存放地址的变量
指针和指针变量是两个不同的概念
但是要注意:通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样
为什么使用指针
直接访问硬件 (opengl 显卡绘图)
快速传递数据(指针表示地址)
返回一个以上的值(返回一个数组或者结构体的指针)
表示复杂的数据结构(结构体)
方便处理字符串
*号的三种含义
*号的含义
数学运算符: 3 * 5
定义指针变量: int * p;
指针运算符(取值): *p (取p的内容(地址)在内存中的值)
指针练习互换两个数字
swap(int* i, int* j) { // 引用传递 int temp = *i; *i = *j; *j = temp; } main() int i = 89; int j = 10; swap(&i, &j); printf("i=%d\n", i); printf("j=%d\n", j); system("pause"); }
指针练习函数返回一个以上的值
change(int* a, int* b) { *a = 1; *b = 2; } main() { int a = 3, b = 5; change(&a, &b); printf(“a=%d, b=%d\n”, a, b); system(“pause”); }
指针和数组的关系
数组名
int a[5]; // a是数组名,5是数组的大小,元素个数
数组名称是个指针常量,它存放的是数组中第一个元素的地址
int a[5];
&a[0] 等价于 &a
下标和指针的关系
int a[5];
a[i] 等价于 *(a + i) // 这里i的范围是0~4(数组长度 -1)
指针的长度
不管什么类型的指针都是4个字节.(64位系统8字节)
C语言为了方便指针运算, 定义各种基本类型的指针, 每种类型的指针运算时所偏移量的值是根据类型的长度决定的.
多级指针
int i = 10; int* p1 = &i; // 一级指针 int** p2 = &p1; // 二级指针 int*** p3 = &p2; // 三级指针 int**** p4 = &p3; // 四级指针 ****p4 = 99; // 修改变量i的值为99;
主函数获取子函数变量地址
void function(int** p){ int i = 4; *p = &i; printf("子函数打印i的地址为%#x\n", &i); } main(){ int* mainp; function(&mainp); printf("主函数打印i的地址为%#x\n", mainp); printf("i的值为%d\n", *mainp); system("pause"); }
静态内存和动态内存
静态内存是系统是程序编译执行后系统自动分配,由系统自动释放, 静态内存是栈分配的.
动态内存是开发者手动分配的, 是堆分配的.
栈内存
* 系统自动分配
* 系统自动销毁
* 连续的内存区域
* 向低地址扩展
* 大小固定
* 栈上分配的内存称为静态内存
堆内存
* 程序员手动分配
* java:new * c:malloc
* 空间不连续
* 大小取决于系统的虚拟内存
* C:程序员手动回收free
* java:自动回收
* 堆上分配的内存称为动态内存
申请堆内存
malloc(memory allocate) 函数
malloc 申请一块堆内存
free(地址); 回收内存
回收malloc 申请的堆内存
realloc re- allocate
重新申请一块堆内存
结构体
struct Student { int age; float score; char sex; }; main() { struct Student stu = {18, 88.5, 'M'}; }
使用结构体变量
struct Student stu = {80,55.5,'F'}; struct Student stu2; stu2.age = 10; stu2.score = 88.8f; stu2.sex= ‘M'; printf("%d %f %c\n", st.age, st.score, st.sex); // 结构体指针 struct Student * pStu; pStu = &stu; pStu->age 在计算机内部会被转换为 (* pStu).age
pStu ->age的含义: pStu所指向的结构体变量中的age这个成员
函数的指针
1.定义int (*pf)(int x, int y);
2.赋值 pf = add;
3.引用 pf(3,5);
4.结构体中不能定义函数,只能声明函数指针
Union 联合体
struct Date { int year; int month; int day; }; union Mix { long i; int k; char ii; }; main() { printf("date:%d\n",sizeof(struct Date)); printf("mix:%d\n",sizeof(union Mix)); system("pause"); }
枚举
enum WeekDay { Monday=0,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday }; main() { enum WeekDay day = Sunday; printf("%d\n",day); system("pause"); }
typedef
声明自定义数据类型,配合各种原有数据类型来达到简化编程的目的的类型定义关键字。
#include <stdio.h> #include <stdlib.h> typedef int i; typedef long l; main() { i m = 10; l n = 123123123; printf("%d\n", m); printf("%ld\n", n); system("pause"); }
JNI 协议
NDK HelloWorld
1.创建一个android工程
2.java代码中写声明native方法
3.创建jni目录,编写c代码,方法名字要对应
4.编写Android.mk文件
5.NDK编译生成动态链接库
6.java代码load动态库.调用native代码
javah 生成头文件
注意: 不同版本的JDK操作方式不同.
命令: javah <包名+类名>
JDK1.6使用方式
在工程的bin/classes目录下, 执行javah命令.
JDK1.7使用方式
在工程的src目录下, 执行javah命令.
NDK 简便开发流程
1. 关联NDK: Window -> Preferences -> Android -> NDK
2. 创建Android工程, 声明native方法.
3. 设置函数库名字: 右键工程 -> Android Tools -> App Native support
4. 使用javah生成.h的头文件, 并把.h文件拷贝到工程下jni文件夹中.
5. c代码提示: 右键工程 -> Properties -> C/C++ General -> Path and Symbols // Includes -> Add -> File system 选中以下路径.latforms\android-18\arch-arm\usr\include
6. 把后缀名.cpp改成.c, 实现native方法.
7. java代码中加载动态库, 调用native方法.
java 与 c之间的数据传递
public native int add(int x, int y);
public native String sayHelloInC(String s);
public native int[] arrElementsIncrease(int[] intArray);
在c代码中使用logcat
Android.mk文件增加以下内容
LOCAL_LDLIBS += -llog
C代码中增加以下内容
#include <android/log.h #define LOG_TAG "System.out" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) C代码中使用logcat, 例: LOGI("info\n"); LOGD("debug\n");
C语言调用java方法
C调用java空方法
public void helloFromJava(){
}
C调用java中的带两个int参数的方法
public int add(int x,int y) {
}
C调用java中参数为string的方法
public void printString(String s){
}
JNI 方法签名
签名类型 Java类型
Z boolean
B byte
C char
S short
I int
J long
F float
D double
L全类名; 引用类型
[类型数组
C语言回调java静态方法
C语言调用下面静态方法.
public static void sayHello(String text) { System.out.println("MainActivity:showText: " + text); }
C语言回调java刷新界面
C语言调用下面Activity中方法, 弹出吐司.
public void showToast(String text) { Toast.makeText(this, text, 0).show(); }
eclipse找不到ndk选项解决办法
NDK插件 com.android.ide.eclipse.ndk_23.0.2.1259578.jar 如果在eclipse里配置ndk却发现没有配置的选项,则需要此插件,放置在eclipse/plugins下,重启即可。
AM命令
am命令 :在adb shell里可以通过am命令进行一些操作如启动activity Service 启动浏览器等等
am命令的源码在Am.java中, 在adb shell里执行am命令实际上就是启动一个线程执Am.java的main方法,am命令后面带的参数都会当作运行时的参数传递到main函数中
am命令可以用start子命令,并且带指定的参数
常见参数: -a: action -d data -t 表示传入的类型 -n 指定的组件名字
举例: 在adb shell中通过am命令打开网页
am start –user 0 -a android.intent.action.VIEW -d http://www.baidu.com
通过am命令打开activity
am start –user 0 -n com.ngyb.cpphello/com.ngyb.cpphello.MainActivity
execlp
execlp c语言中执行系统命令的函数
execlp() 会从PATH环境变量所指的目录中查找符合参数file的文件找到后就执行该文件, 第二个参数开始就是执行这个文件的 args[0],args[1] 最后一个参数用(char*)NULL结束
android开发中 execlp函数对应android的path路径为
system/bin/目录
调用格式
execlp(“am”, “am”, “start”, “–user”,”0”,”-a”, “android.intent.action.VIEW”, “-d”, “http://www.baidu.com“, (char *) NULL);
execlp(“am”, “am”, “start”, “–user”,”0”, “-n” , “com.ngyb.cforktest/com.ngyb.cforktest.MainActivity”,(char *) NULL);
什么是JNI
JNI(Java Native Interface) java本地开发接口
JNI 是一个协议, 有了这个协议可以使Java代码和C/C++代码相互调用.
为什么用JNI
1、扩展JVM功能,访问驱动,操作硬件(物联网、智能家居、
车载电脑)
2、效率上c/c++语言效率更高数学运算,实时渲染的游戏上,音
视频处理
3、 java反编译比c语言容易, 通过JNI增强代码安全性(加密算法放到C实现)
4、代码移植,复用已经存在的c代码(opencv,ffmpeg…)
怎么用JNI
1.C/C++语言
2.掌握java jni流程
3.NDK (native develop kits )
开发环境介绍
windows下用轻量级 dev-c++
C语言基本数据类型
Java 八大基本数据类型 C基本数据类型
boolean
byte
char char
int int
float float
double double
long long
short short
C没有boolean byte
signed unsigned 有符号无符号修饰符只能修饰整形变量
signed 有符号最高位符号位可以表示负数
unsigned 无符号最高位仍然是数值位不可以表示负数
输入输出函数
输出函数
printf(“要输出的内容+占位符”,…….)
常用占位符
%d - int
%ld – long int
%lld - long long
%hd – 短整型
%c - char
%f - float
%lf – double
%u – 无符号数
%x – 十六进制输出 int 或者long int 或者short int
%o - 八进制输出
%s – 字符串
输入函数 Scanf
Int len;
Scanf(“%d”,&len); &取地址符号
什么是指针
int main(void)
{
int * p; //p 是变量的名字, int * 是一个类型
//这个变量存放的是int类型变量的地址。
int i =3;
p=&i;
system(“pause”);
return 0;
}
指针常见错误
指针变量声明后未赋值直接用
声明的指针类型和指向的类型要一致
指针和指针变量的关系
指针就是地址,地址就是指针
地址就是内存单元的编号
指针变量是存放地址的变量
指针和指针变量是两个不同的概念
但是要注意:通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样
为什么使用指针
直接访问硬件 (opengl 显卡绘图)
快速传递数据(指针表示地址)
返回一个以上的值(返回一个数组或者结构体的指针)
表示复杂的数据结构(结构体)
方便处理字符串
*号的三种含义
*号的含义
数学运算符: 3 * 5
定义指针变量: int * p;
指针运算符(取值): *p (取p的内容(地址)在内存中的值)
指针练习互换两个数字
swap(int* i, int* j) { // 引用传递
int temp = *i;
*i = *j;
*j = temp;
}
main() {
int i = 89;
int j = 10;
swap(&i, &j);
printf("i=%d\n", i);
printf("j=%d\n", j);
system("pause");
}
指针练习函数返回一个以上的值
change(int* a, int* b) {
*a = 1;
*b = 2;
}
main() {
int a = 3, b = 5;
change(&a, &b);
printf(“a=%d, b=%d\n”, a, b);
system(“pause”);
}
指针和数组的关系
数组名
int a[5]; // a是数组名,5是数组的大小,元素个数
数组名称是个指针常量,它存放的是数组中第一个元素的地址
int a[5];
&a[0] 等价于 &a
下标和指针的关系
int a[5];
a[i] 等价于 *(a + i) // 这里i的范围是0~4(数组长度 -1)
指针的长度
不管什么类型的指针都是4个字节.(64位系统8字节)
C语言为了方便指针运算, 定义各种基本类型的指针, 每种类型的指针运算时所偏移量的值是根据类型的长度决定的.
多级指针
int i = 10;
int* p1 = &i; // 一级指针
int** p2 = &p1; // 二级指针
int*** p3 = &p2; // 三级指针
int**** p4 = &p3; // 四级指针
****p4 = 99; // 修改变量i的值为99;
主函数获取子函数变量地址
void function(int** p){
int i = 4;
*p = &i;
printf("子函数打印i的地址为%#x\n", &i);
}
main(){
int* mainp;
function(&mainp);
printf("主函数打印i的地址为%#x\n", mainp);
printf("i的值为%d\n", *mainp);
system("pause");
}
静态内存和动态内存
静态内存是系统是程序编译执行后系统自动分配,由系统自动释放, 静态内存是栈分配的.
动态内存是开发者手动分配的, 是堆分配的.
栈内存
* 系统自动分配
* 系统自动销毁
* 连续的内存区域
* 向低地址扩展
* 大小固定
* 栈上分配的内存称为静态内存
堆内存
* 程序员手动分配
* java:new * c:malloc
* 空间不连续
* 大小取决于系统的虚拟内存
* C:程序员手动回收free
* java:自动回收
* 堆上分配的内存称为动态内存
申请堆内存
malloc(memory allocate) 函数
malloc 申请一块堆内存
free(地址); 回收内存
回收malloc 申请的堆内存
realloc re- allocate
重新申请一块堆内存
结构体
struct Student {
int age;
float score;
char sex;
};
main() {
struct Student stu = {18, 88.5, 'M'};
}
使用结构体变量
struct Student stu = {80,55.5,'F'};
struct Student stu2;
stu2.age = 10;
stu2.score = 88.8f;
stu2.sex= ‘M';
printf("%d %f %c\n", st.age, st.score, st.sex);
// 结构体指针
struct Student * pStu;
pStu = &stu;
pStu->age 在计算机内部会被转换为 (* pStu).age
pStu ->age的含义: pStu所指向的结构体变量中的age这个成员
函数的指针
1.定义int (*pf)(int x, int y);
2.赋值 pf = add;
3.引用 pf(3,5);
4.结构体中不能定义函数,只能声明函数指针
Union 联合体
struct Date {
int year;
int month;
int day;
};
union Mix {
long i;
int k;
char ii;
};
main() {
printf("date:%d\n",sizeof(struct Date));
printf("mix:%d\n",sizeof(union Mix));
system("pause");
}
枚举
enum WeekDay {
Monday=0,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
};
main() {
enum WeekDay day = Sunday;
printf("%d\n",day);
system("pause");
}
typedef
声明自定义数据类型,配合各种原有数据类型来达到简化编程的目的的类型定义关键字。
#include <stdio.h>
#include <stdlib.h>
typedef int i;
typedef long l;
main() {
i m = 10;
l n = 123123123;
printf("%d\n", m);
printf("%ld\n", n);
system("pause");
}
JNI 协议
NDK HelloWorld
1.创建一个android工程
2.java代码中写声明native方法
3.创建jni目录,编写c代码,方法名字要对应
4.编写Android.mk文件
5.NDK编译生成动态链接库
6.java代码load动态库.调用native代码
javah 生成头文件
注意: 不同版本的JDK操作方式不同.
命令: javah <包名+类名>
JDK1.6使用方式
在工程的bin/classes目录下, 执行javah命令.
JDK1.7使用方式
在工程的src目录下, 执行javah命令.
NDK 简便开发流程
1. 关联NDK: Window -> Preferences -> Android -> NDK
2. 创建Android工程, 声明native方法.
3. 设置函数库名字: 右键工程 -> Android Tools -> App Native support
4. 使用javah生成.h的头文件, 并把.h文件拷贝到工程下jni文件夹中.
5. c代码提示: 右键工程 -> Properties -> C/C++ General -> Path and Symbols // Includes -> Add -> File system 选中以下路径.latforms\android-18\arch-arm\usr\include
6. 把后缀名.cpp改成.c, 实现native方法.
7. java代码中加载动态库, 调用native方法.
java 与 c之间的数据传递
public native int add(int x, int y);
public native String sayHelloInC(String s);
public native int[] arrElementsIncrease(int[] intArray);
在c代码中使用logcat
Android.mk文件增加以下内容
LOCAL_LDLIBS += -llog
C代码中增加以下内容
#include <android/log.h>
#define LOG_TAG "System.out"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
C代码中使用logcat, 例:
LOGI("info\n");
LOGD("debug\n");
C语言调用java方法
C调用java空方法
public void helloFromJava(){
}
C调用java中的带两个int参数的方法
public int add(int x,int y) {
}
C调用java中参数为string的方法
public void printString(String s){
}
JNI 方法签名
签名类型 Java类型
Z boolean
B byte
C char
S short
I int
J long
F float
D double
L全类名; 引用类型
[类型数组
C语言回调java静态方法
C语言调用下面静态方法.
public static void sayHello(String text) {
System.out.println("MainActivity:showText: " + text);
}
C语言回调java刷新界面
C语言调用下面Activity中方法, 弹出吐司.
public void showToast(String text) {
Toast.makeText(this, text, 0).show();
}
eclipse找不到ndk选项解决办法
NDK插件 com.android.ide.eclipse.ndk_23.0.2.1259578.jar 如果在eclipse里配置ndk却发现没有配置的选项,则需要此插件,放置在eclipse/plugins下,重启即可。
AM命令
am命令 :在adb shell里可以通过am命令进行一些操作如启动activity Service 启动浏览器等等
am命令的源码在Am.java中, 在adb shell里执行am命令实际上就是启动一个线程执Am.java的main方法,am命令后面带的参数都会当作运行时的参数传递到main函数中
am命令可以用start子命令,并且带指定的参数
常见参数: -a: action -d data -t 表示传入的类型 -n 指定的组件名字
举例: 在adb shell中通过am命令打开网页
am start –user 0 -a android.intent.action.VIEW -d http://www.baidu.com
通过am命令打开activity
am start –user 0 -n com.ngyb.cpphello/com.ngyb.cpphello.MainActivity
execlp
execlp c语言中执行系统命令的函数
execlp() 会从PATH环境变量所指的目录中查找符合参数file的文件找到后就执行该文件, 第二个参数开始就是执行这个文件的 args[0],args[1] 最后一个参数用(char*)NULL结束
android开发中 execlp函数对应android的path路径为
system/bin/目录
调用格式
execlp(“am”, “am”, “start”, “–user”,”0”,”-a”, “android.intent.action.VIEW”, “-d”, “http://www.baidu.com“, (char *) NULL);
execlp(“am”, “am”, “start”, “–user”,”0”, “-n” , “com.ngyb.cforktest/com.ngyb.cforktest.MainActivity”,(char *) NULL);