JNI
- java native interface
Java基本数据类型
- byte
- short
- int
- long
- float
- double
- char
- boolean
符号位
- signed:分正负
- unsigned:不分正负
int取值范围
- int数据:4个字节,32位
- 能表示的数字是 2的32次方 个数字
- 取值范围 0 ~ 2的32次方-1
- 最高位取出来作为符号位,用于表示正负,不再表示数值,剩下31个数值位
- 少了一位,当前能表示的数字是 2的31次方 个
- 取值范围 0 ~ 2的31次方-1
- 符号位如果为0,那么31个数值位表示的数字是正数,符号位如果为1,31个数值位表示出来的是负数
- 结合符号位,int型的取值范围 -2的31次方 ~ 2的31次方-1
- 无符号就是最高位不表示符号位,依然作为数值位使用
原反补
- 原码:二进制表示
- 反码:除最高位,其他位依次取反
- 补码:反码+1
- 正数原反补是一样的
C基本数据类型
- short:2
- int:4
- long:4
- float:4
- double:8
- char:1
内存地址
- 内存的门牌号
- 内存中所有数据都是通过地址拿到的
- 没有地址的内存是无法访问
- 32位系统能表示的最大内存是4G
- 因为32位系统的内存总线长度是32位,也就是可以用于分配给内存作为地址的数字是 2的32次方 个
- 内存中每个字节都有一个地址
内存修改器
- 原理:比方说修改血量,血量一定是保存在内存中的一个变量,找到这个变量的地址,修改地址上的值
指针变量
- 保存的值是一个内存地址
- 定义方式 int* p:指针指向的内存地址上存放的数据是一个int型
- *p:引用指针指向的内存地址上存放的数据
- int** q:定义二级指针,二级指针存放的是一个一级指针的地址
常见错误
- 指针赋值之前,不要使用(给*p赋值)
- 未赋值的指针称为野指针
- 指针的类型不要混用
值传递和引用传递
- 值传递:传递一个普通的值
- 引用传递:传递一个内存地址,其实所有传递都叫值传递,引用传递传递的值是一个地址
数组
- 数组的内存空间是连续的
- 数组变量保存的是首地址(第0个元素的地址)
- *
栈
- 内存空间是连续的
- 自动分配
- 大小固定
- 系统自动释放
- 栈上分配的内存叫静态内存
堆
- 程序员手动申请
- java:new
- c:malloc
- 大小不固定,取决于系统虚拟内存
- 程序员手动释放
- java:自动
- c:free函数
- 空间不连续
- 堆上分配的内存叫动态内存
交叉编译
- 在一个平台下编译出另一个平台可以执行的二进制程序
- CPU平台:arm,x86,mips
- 系统平台:Windows、Linux、Mac OS
- 原理:模拟另一个平台的特性去编译代码
- 源代码->预编译->编译->链接->可执行程序
- 工具链:一个工具使用完毕自动使用下一个
常见工具
- NDK:native development kits
- CDT:C/C++ development tools
- eclipse插件
- 高亮显示C关键字
- cygwin:Windows平台下的Linux命令行模拟器
NDK目录结构
- docs:帮助文档
- build/tools:Linux批处理文件
- platforms:存放开发jni用到的h头文件和so动态链接库
- prebuilt:预编译使用的工具
- sample:使用jni的案例
- source:NDK的部分源码
- toolchains:工具链
- ndk-build.cmd:编译打包C代码的指令
JNI
步骤
- 定义并调用本地方法
- 创建jni文件夹
- jni文件夹里创建c文件
c文件中实现本地方法,格式如下
//返回值与本地方法一致 //函数名:Java_包名_类名_本地方法名 //env:结构体二级指针,该结构体中封装了大量的函数指针,可以帮助程序员实现某些常用功能 //thiz:本地方法调用者的对象(MainActivity的对象) jstring Java_com_itheima_helloworld1_MainActivity_helloFromC(JNIEnv* env, jobject thiz)
- 创建Android.mk文件,指定要编译的c文件
- 在jni目录下,执行ndk-build.cmd,编译打包出so动态链接库
- 在java代码中加载动态链接库
- 部署,运行
常见错误
- 找不到类库
- 没有添加对应平台的支持
- 类库名写错了
- 找不到本地方法
- 忘记加载类库
- c函数名写错了
javah指令
- 自动生成jni样式的头文件,头文件中就包含了我们需要的函数名
- 1.7:在src目录下使用:javah com.itheima.helloworld2.MainActivity
- 1.6:在bin/classes目录下使用:
添加本地支持
- 自动生成jni文件夹
- 自动生成c文件和Android.mk文件
- 指定jni.h头文件的路径,相当于关联源码
- 不需要再去jni目录下使用ndk-build.cmd指令,项目部署时,会先打包编译so类库再去部署到手机上
数组传递
- java的数组是对象,传递对象是传递对象的地址,c函数中修改了地址上的值,所以数组的值就改变了
javap
- 打印指定类中所有方法的签名
- 在bin/classes目录下使用:javap -s com.itheima.helloworld2.MainActivity
自定义竖型进度条
- 自定义控件,继承View
- 三个构造方法
- 布局文件中使用自定义控件要写包名
- onDraw方法用于在组件内绘制内容,图形或者文字都是通过这个方法绘制到界面上的
C++实现JNI
- C++中的JNIEnv和C的JNIEnv不是同一个结构体
- C++的 JNIEnv 是jni.h中定义的 _JNIEnv
- C的 JNIEnv 是jni.h中定义的 JNINativeInterface*
- _JNIEnv结构体中的函数其实就是调用了JNINativeInterface中的同名函数指针
- C++中函数要先声明才能使用
分支C进程
- fork函数分支一个C进程,返回子进程的pid
- 子进程执行fork函数时不会再分支进程了,返回0