什么是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#include 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
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 通过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”, ““, (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 通过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”, ““, (char *) NULL);
execlp(“am”, “am”, “start”, “–user”,”0”, “-n” , “com.ngyb.cforktest/com.ngyb.cforktest.MainActivity”,(char *) NULL);