Android下JNI开发

什么是JNI

  • JNI java native interface native本地 java本地接口

  • 通过JNI可以实现java和本地代码之间相互调用

  • jni可以看做是翻译 实际上就是一套协议

为什么要用JNI

  1. 市场需求

  2. 让java代码和底层代码之间互相调用

  • java调用底层特殊硬件(调用c语言,车载电脑)

  • 效率上c/c++语言效率更高(时间和内存要求严格的场景)

  • 复用已经存在的c代码, c语言发展了几十年有很多优秀的代码库(ffmpeg,opencv,7zip)

  • java反编译非常容易.c语言反编译不容易.关键业务逻辑需要用c实现.

  • 历史遗留问题,复用原来pc端的c代码

怎么用JNI

  • 熟悉java

  • c/c++ 能看懂 会调用(2级等级考试程度)

  • JNI规范(开发流程 NDK native develop kit)

实际应用场景 (只要是和硬件打交道,必定涉及到驱动,物联网)

开发环境搭建

C基本语法

CHelloWorld

#include<stdio.h>    // 相当于 java的import .h c的头文件 stdio.h standard io 标准输入输出 
#include<stdlib.h>   // stdlib standard library 标准函数库    java.lang 
/**
*/
main(){    // public static void main(String[] args)
       printf("helloworld!\n");  //System.out.println();   "\n"换行符 
       system("javac Hello.java");
       system("java Hello");
       system("notepad");
       system("pause"); //system执行windows的系统命令 
       } 

C的基本数据类型

  • java基本数据类型

  • boolean 1

  • byte 1

  • char 2

  • short 2

  • int 4

  • long 8

  • float 4

  • double 8

演示不同类型所占用的字节数

  • c基本数据类型

  • char 1个字节

  • short 2

  • int 4

  • long 4

  • float 4

  • double 8

  • void 1

  • c的基本类型char, int, float, double, long, short, signed, unsigned, void

总结:

  • c语言中的char可以用java的byte代替

  • c语言中的int ,float , double ,short 和java完全一样,可以相互代替

  • c语言的long ,用java的int代替

  • java中的byte和boolean类型,c语言里面没有.

  • java中的byte可以用c语言中的char表示

  • java中的boolean类型,c语言用0 false和非0 true

  • java中的long类型,c语言用 long long类型表示.

signed,unsigned 只能修饰整形

C的输出函数


%d  -  int
%ld – long int
%lld - long long
%hd – 短整型
%c  - char
%f -  float
%lf – double
%u – 无符号数
%x – 十六进制输出 int 或者long int 或者short int
%o -  八进制输出
%s – 字符串

16进制和8进制表示方法

  • C字符串

    • C没有String类型 C的字符串实际就是字符数组

    • C数组定义 [ ]只能再变量名之后

    • C字符串两种定义方式

      char str[] = {'h','e','l','l','o','\0'};//注意'\0'字符串结束符

      
      char str[] = "你好"; //这种定义方式不用写结束符 可以表示汉字

C的输入函数

输入一个班级的人数

输入一个班级的名称

注意数组越界

  • scanf("占位符", &地址);

  • & 取地址符

  • C字符串不检查下标越界 使用时要注意

指针入门

指针就是一个地址,地址就是一个指针

QQ连连看

指针变量:用来存放一个地址的变量.用来存储某种数据在内存中的地址.

如果一个* 在指针变量的前面,代表的含义就是把这个地址里面的数据取出来

*号的三种含义

* 号的第一种含义表示的是乘法操作.


int i = 3;
int j = 5;
i * j相乘

* 号的第二种含义


如果一个星号是在一种数据类型的后面
代表的就是这种数据类型的指针变量,
用来存放这种数据类型在内存中的地址.
int i = 3;
int*  p; 可以存放i的地址
p=&i;

* 号的第三种含义


如果星号在一个指针变量的前面
代表的就是把这个地址里面的数据取出来.
int*  p;    
*p; 把p地址里面的数据取出来

  • 画图分析指针和地址间的关系

  • 指针常见错误

    • 声明了指针变量后 未初始化直接通过*p 进行赋值操作

        • 未赋值的指针称为野指针

    • 指针类型错误 如int* p 指向了double类型的地址, 通过指针进行读取操作时,读取值会出错

内存地址的概念

  • 声明一个变量,就会立即为这个变量申请内存,一定会有一个对应的内存地址

  • 没有地址的内存是无法使用的

  • 内存的每一个字节都有一个对应的地址

  • 内存地址用一个16进制数来表示

  • 32位操作系统最大可以支持4G内存

    • 32位系统的地址总线为32位,也就是说系统有2^32个数字可以分配给内存作为地址使用

指针的练习

  • 交换两个变量值的最基本写法

  • 把前面交换值的逻辑抽取一个方法去写,发现值没有发生变化

  • 换成Java代码,在eclpse运行,值还是没有发生交换

  • 在Java中,以下面的方式交换数据可以交换

  • 传递失败的原因是子函数所申请的内存空间,在子涵数运行完毕后,全部释放

  • 将普通的值传递改为引用传递后能够改变值.

  • 画图分析通过传地址后值交换的过程(Java里面对象传递,实际上是引用传递,也就是地址传递)

  • 值传递和引用传递(交换两个数的值)

    • 引用传递本质是把地址传递过去

    • 所有传递其实本质都是值传递,引用传递其实也是传递一个值,但是这个值是一个内存地址

      void swap(int* p, int* p2){

      
          int temp = *p;
          *p = *p2;
          *p2 = temp; 
      }
      main(){
          int i = 123;
          int j = 456;
          //将i, j的地址传递过去
          swap(&i,&j);
          printf("i = %d, j = %d", i, j);
      }
  • 看代码区分是java程序员还是C程序员写代码(google工程师很多的android程序员都是由C程序员转过来的,从代码中就能看到C语言的特性,函数运行完毕没有返回值,一般是C语言的特点).也就是把地址作为参数传入函数中,当函数执行完毕时,参数的值就已经被修改了

  • 返回多个值

    • 把地址作为参数传入函数中,当函数执行完毕时,参数的值就已经被修改了

多级指针

  • int* p; int 类型的一级指针 int** p2; int 类型的二级指针

  • 二级指针变量只能保存一级指针变量的地址

  • 有几个* 就是几级指针 int*** 三级指针

  • 通过int类型三级指针 操作int类型变量的值 ***p

    int i = 123;

    
        //int类型一级指针 
        int* p = &i;
        //int 类型 二级指针 二级指针只能保存一级指针的地址 
        int** p2 = &p;
        //int 类型 三级指针  三级指针只能保存二级指针的地址 
        int*** p3 = &p2;
        //通过p3 取出 i的值
        printf("***p3 = %d\n", ***p3);
  • 多级指针案例 取出子函数中临时变量的地址(在主函数中取出子函数中一级指针中变量的地址值)

  • 画图讲解内存地址的赋值过程

*幻影数据,旧数据,无效数据(将内存条放入液氮中,可以将旧数据取出)

数组和指针的关系

  • 打印数组中的第0个元素,注意[]放的位置

  • C语言中的数组不检查是否越界,所以一旦给一个越界的索引,获取的是随机的值

  • 创建一个子函数,专门用来打印数组,在子函数中,确定索引的下标,防止数组越界

  • 数组占用的内存空间是连续的

  • 数组变量保存的是第0个元素地址,也就是首地址

  • int数组在内存中的的地址.每个元素相隔4位,因为每一个int值占四位(也就是说每个元素占据的byte的内存空间,跟数组的数据类型有关)

  • *(p + 1):指针位移一个单位,一个单位是多少个字节,取决于指针的类型

指针的长度

  • 不管变量的类型是什么,它的内存地址的长度一定是相同的

  • 内存地址不能相减

  • 所有的语言,如果需要执行,都必须先被装载在内存中,所有不同的类型只决定变量占用的内存空间不同

  • 32位环境下,内存地址长度都是4个字节,所以指针变量长度只需4个字节即可


  • 区分指针类型是为了指针位移运算方便

堆栈概念 静态内存分配 动态内存分配

  • 栈内存

    • 系统自动分配

    • 系统自动销毁

    • 连续的内存区域

    • 向低地址扩展

    • 大小固定

    • 栈上分配的内存称为静态内存

  • 静态内存分配

  • 子函数执行完,子函数中的所有局部变量都会被销毁,内存释放,但内存地址不可能被销毁,只是地址上的值没了

  • 堆内存

    • 程序员手动分配

      • java:new

      • c:malloc

    • 空间不连续

    • 大小取决于系统的虚拟内存

    • C:程序员手动回收free

    • java:自动回收

    • 堆上分配的内存称为动态内存

  • C语言版本学生管理系统,通过申请内存,动态申请内存

    • 申请固定的内存大小

    • 追加申请更多内存

  • 多级指针取变量值(如何取到 i的值)

  • 多级指针在Java中的应用举例(开发一款网游游戏)

结构体(类似java中的类,把一些方法和数据封装在一起,这个时候就有了一个种结构体的出现,结构体是java类的老祖宗,java中的类就是由C语言中的结构体演化过来的,结构体的使用,基本上和java中的类的使用一样)

  • 定义一个简单结构体

  • 使用结构体,调用结构体

  • 结构体中的属性长度会被自动补齐,这是为了方便指针位移运算(这个是编译器内部做的处理)

  • 结构体中不能定义函数

*结构体中可以声明一个函数

*结构体中使用函数方法1

*结构体中使用函数方法2

*结构体中使用函数方法3

  • 提高可读性,名字最好相同

  • 程序运行时,函数也是保存在内存中的,也有一个地址

  • 结构体中只能定义变量

  • 函数指针其实也是变量,它是指针变量

  • 函数指针的定义 返回值类型(*变量名)(接收的参数);

  • 函数指针的赋值: 函数指针只能指向跟它返回值和接收的参数相同的函数

联合体(类似java中的泛型,可以表示多钟类型)

  • 长度等于联合体中定义的变量当中最长的那个

枚举(和java中的枚举使用一模一样)


  #include <stdio.h>
 enum WeekDay
  {
  Monday=8,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
  };
  
  main(void)
  {
    //int day;
   enum WeekDay day = Sunday;
   printf("%d\n",day);
   system("pause");
   return 0;
 }

类型定义(自定义类型),就是给类型取别名,注意格式!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值