JNI

配置NDK,调用JNI最终会生成一个so库,如果so库生成了。直接在项目中使用so库即可调用本地方法。注意:api的包名要与so库定义的包名一致。




1什么是jni

 jni java native interface java本地开发接口,是JAVA和C互相调用的桥梁。


2jni有什么用

 java语言不可以直接操作硬件,但是C可以,通过JNI,JAVA也就能操作硬件。 JAVA操作硬件的API,基本上都是由C和C++去写的,然后由JNI实现JAVA去调用。

(1)wifi热点共享

(2)suv的车窗

(3)不重复造轮子 

(4)FFmpeg音视频解码  vitamio框架

(5)webkit 浏览器的内核

(6)opencv 图像库 

(7)7Zip

3怎么用jni

JAVA + C + JNI + NDK

      

学习路线:

C语言学习资源

http://www.dotcpp.com/oj/classc.html


CodeBlocks的安装

http://www.jianshu.com/p/d5fa463df77d

1)千万注意下载带MinGW的版本

2)软件安装之后进入settings-compiler-ToolChain Excutable,H点击AutoDetect

  否则工程无法运行。

3)快捷键配置http://blog.csdn.net/wr132/article/details/48002887

4)格式化代码:http://blog.csdn.net/qwb492859377/article/details/46933185





第1天:C语言复习

学习C不光是为了JNI,很多算法都是用C来演示的,而且JAVA的底层都是C,学习C很必要。


  1. 搭建开发环境

    dev-c++,体积小,win7和win8需要装不同的版本,否则编译会出莫名的错误。 

  2. 基本数据类型、数组的内存地址

    基本数据类型内存地址: &变量名,表示取地址。

    数组的内存地址:连续,数组名就是第一个元素的地址,数组就是一种类型指针。

  3. 指针


■概念:指针就是内存地址,内存地址就是指针

1
2
3
4
5
6
7
8
9
10
11
12
13
main()
{
      int  i = 4;   //申请了一块内存空间  空间的别名叫i  这块空间里面存的数据是 4 
       
        
      //需求 我想把i的地址给存起来  
      int * p =  &i; 
      printf ( "p的地址%#x\n" ,p); 
      
      
      system ( "pause" );             //执行一个windos系统的外部命令  让dos窗体暂停退出 
      
}

*号的三种含义

1
2
3
4
5
6
1 *代表乘法  
2 如果*号放到一种数据类型的后面 那么就代表这种数据类型的指针 比如  int *   float
   当然,类型还可以是更为复杂的数据类型,如结构体,枚举,联合体。
   类型当然也可以是指针本身,这样就形成了多级指针。  
3 *号 如果放到指针变量的前面   代表取出该变量里面的地址的对应的数据 
   *指针变量 == 指针变量所指向的变量


指针的类型和长度

1
2
3
不管什么类型的指针都是4个字节(32位操作系统),对于64位操作系统是8个字节。
C语言为了方便指针运算, 定义各种基本类型的指针, 每种类型的指针运算时所偏移量的值是根据类型的长度决定的
+1 移动的是一个单位


■数组与指针

 

1
数组名就是指针,代表数组的首地址。可以采用地址来遍历数组


■字符串与指针

 

1
2
3
4
char  *str =  "www.dotcpp.com"  ;
  
  字符指针可以采用字符串常量来初始化
  字符串指针指向字符串常量的首地址,*str打印出来是w,并不是整个字符串。

 


■多级指针

1
在一种数据类型的后面 有几个* 就代表几级指针

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int  main()
{
     int  **p;   
     int  i,j;    //p[4][8] 
     //开始分配4行8列的二维数据   
     p =  new  int  *[4];
     for (i=0;i<4;i++){
         p[i]= new  int  [8];   
     }
 
     for (i=0; i<4; i++){
         for (j=0; j<8; j++){
             p[i][j] = j*i;
         }
     }   
     //打印数据   
     for (i=0; i<4; i++){
         for (j=0; j<8; j++)     
         {   
             if (j==0) cout<<endl;   
             cout<<p[i][j]<< "\t" ;   
         }
     }   
     //开始释放申请的堆   
     for (i=0; i<4; i++){
         delete  [] p[i];   
     }
     delete  [] p;   
     return  0;
}
上面的例子中
int  *p :p表示指向 int 型的指针
int  **p: p表示指向 int  *(指针)的指针,即指向指针的指针。

■函数指针

1
2
3
4
5
6
7
8
9
      和函数头一样,其它不变,只要将函数名改为(*函数别名)即可。如:
  定义一个函数
      int  add( int  x , int  y)
      {
         return  x + y;
      }     
      定义上面这种函数的指针
      int  (*func) ( int  x , int  y) = add;
      函数指针的作用--->


■指针的常见错误

 

1
2
(1) 定义的数据类型 要和你定义的指针类型要相对应 
(2)指针变量未经赋值  不可以直接使用    野指针


■深入理解指针

wKioL1ZElaKAoui_AAAS0WQYwQ4893.png

   &i:表示取变量i的内存地址

   int* p = &i:表示将变量i的内存地址用int类型指针p存储起来

   *p:表示指针p所指向的内存地址对应的变量(注意最好不要理解成变量的值),也就是*p=*

     (&i)=i,就把*与&理解成可以相互抵消的意思(虽然这么理解不太准确)

   --------------------------------------------------------------------------------------

   所以对于下面的"修改i的值,会不会影响*p"和"修改*p的值,会不会影响i",答案是两个都会,因

   为*p就指向了i,它们是同一块内存地址。

   

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
main()
{
       int  i = 8;
       
       int * p = &i;   //p 是用来 存地址 
       
       //(1)我修改i的值 会不会影响p的值   不会 
       /**
       printf("p的值修改前为%#x\n",p);
        i = 100;
       printf("p的值修改后为%#x\n",p);  **/
       
       //(2)我修改p的值  会不会影响i       不会 
       /**
        printf("i的值修改前为%d\n",i);
        int j = 100; 
        p = &j; 
        printf("i的值修改后为%d\n",i);  **/
        
       //(3)我修改i的值 会不会影响 *p      会 
        /**
          printf("*p的值修改前为%d\n",*p);
          i = 200; 
          printf("*p的值修改后为%d\n",*p); **/
          
        
       //(4)我修改*p 会不会影响i           会 
         printf ( "i的值修改前为%d\n" ,i);
         *p = 100;
          
         printf ( "i的值修改后为%d\n" ,i);
       system ( "pause" );             //执行一个windos系统的外部命令  让dos窗体暂停退出 
}

■动态内存和静态内存

 静态内存是系统是程序编译执行后系统自动分配,由系统自动释放, 静态内存是栈分配的.

  动态内存是开发者手动分配的, 是堆分配的.  你要手动释放


  malloc:动态申请一块内存空间 需要我们手动释放 free();

  realloc:为已有的动态内存再去申请额外的动态内存


1
2
3
4
5
6
7
8
9
10
11
12
13
/*示例*/
     printf ( "亲,请输入学生的数量\n" );
     int  stuNumber;
     scanf ( "%d" ,&stuNumber);
     
     //动态申请内存空间
     int * stuArrs =  malloc (stuNumber* sizeof ( int ));
     //输入每个学生的学号
     int  i ;
     for (i = 0; i < stuNumber;i++){
         printf ( "请输入第%d个学生的学号\n" ,i);
         scanf ( "%d" ,stuArrs + i);
     }

■结构体的两种取值方式

 

1
2
  结构体变量名.元素名称
  结构体指针->元素名称,避免了写多个*,造成混淆。


■自定义数据类型

 

1
2
3
  就是给数据类型定义别名,这样对于比较长的数据类型就可以简写了。
  数据类型可以是任意的,包括指针或多级指针。
  如: typedef  const  struct  JNINativeInterface* JNIEnv;

4.预处理

 ■宏定义

  》实现函数功能,比函数效率更高。

 ■文件包含

  

 


C的模板代码

1
2
3
4
5
6
#include <stdio.h>      // 类似java的导包   引入标准的输入输出 
#include <stdlib.h>     //引入常用函数库 
main(){     
      printf ( "Hello world !\n" );   //相当于java的 System.out.println(); \n代表换行 
      system ( "pause" );             //执行一个windos系统的外部命令  让dos窗体暂停退出 
}

C语言的基本数据类型

 java的八大基本类型所占字节个数

  (1)byte     1

  (2)short    2

  (3)int      4 

  (4)float     4

  (5)double    8

  (6)long     8

  (7)char     2

  (8)boolean   1     

  

C语言的基本数据类型  用sizeof函数来判断数据类型长度

 char 的数据类型长度为:1     和java不一样

  int 的数据类型长度为:4

  float 的数据类型长度为:4

  double 的数据类型长度为:8

  long 的数据类型长度为:4         和java不一样

  short 的数据类型长度为:2

  void   不确定是什么类型 就代表任意类型   

  signed  unsigned 他不是数据类型 是数据类型的修饰符 

      signed 有符号  可以表示负数   -128~127

      unsigned 无符号 没有负数       0~255 

   

  C语言中没有boolean C语言中用0表示假 非0表示真

  在32位操作系统下,任何类型的指针变量都占四个字节!


C语言的输入输出占位符  

    %d  -  int

    %ld - long int

    %c  - char

    %f -  float     默认保留小数点后6位    可以通过.几  

    %u – 无符号数

    %hd – 短整型

    %lf – double

    %x – 十六进制输出 int 或者long int 或者short int

    %o -  八进制输出

    %s – 字符串



C语言中如何定义字符串   

     String   C语言中用字符数组来定义字符串     

     C语言中不检查角标越界   

     定义数组的时候要有一个结束的标志  \0 

  

1
2
3
    char  arr[] = { 'h' , 'e' , 'l' , '\0' , 'l' , 'o' };   
     
    printf ( "您输入的数为:%s" ,arr);    //输出结果为hel,说明'\0'就表示结束了。


学习C++

 1.基础篇章,与C的不同之处。

  1)引用和指针的区别

  2)输入输出

  3)动态内存

  4)命名空间(对应java的包名)

  5)模板(相当于java的泛型)

  6)类(多继承,继承类型)

  7)预处理(预定义宏__LINE__  __FILE__) 

 http://www.runoob.com/cplusplus/cpp-templates.html (接口实现,没有看明白。)

  8)信号处理(java中的接口回调)

  9)多线程(找不到pthread.h这个文件)

  


第2天:JNI的实现

####################################################

AS2.2使用CMake方式进行JNI/NDK开发

http://www.jianshu.com/p/cb3064450688


1.javac 命令将native方法所在的类编译成.class文件

2.javah 生成本地方法的头文件

3.新建.cpp文件,include本地方法的头文件,实现各个方法.

 》由于.cpp是jni方法,所以在最前面要加上include <jni.h>,<jni.h>其实就是用原生的C实现了java与

 C交互一套原则即JNI规范。

 》.cpp本质就是c++文件,只是实现了java native方法,按照一定的命名规则,使native方法可以

 关联到.cpp里的方法。

 


 注意.cpp文件的除了include代码,其它代码都要写在

 extern "C" {


 }模块里.

4.配置CMakeList.txt文件

 往往在调用C的时候,除了工程自己生成的native-lib.cpp文件之外,要自己新建.cpp文件,并且有可能要调用到.c文件.这时候所有的.c和.cpp文件需要在CMakeList里配置好,否则就会报这个错误:

 

 undefined reference to `jlEncodeKeyDev2()' clang++.exe: error: linker command failed with exit code 1 (use -v to see invocation)

 

 比如我的工程CPP目录如下:

 wKiom1nldY_gNmjXAABI0OBWuLo918.png


 正确配置多个cpp的话如下:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
cmake_minimum_required(VERSION 3.4.1)
 
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds it for you.
# Gradle automatically packages shared libraries with your APK.
aux_source_directory(src/main/cpp/ SRC_LIST)           //不会往下递归,所以要配置多个.
aux_source_directory(src/main/cpp/ private  SRC_LIST2)
aux_source_directory(src/main/cpp/ public  SRC_LIST3)
 
add_library( # Sets the name of the library.
              native-lib   #导入时so库的名称,也是native方法被调用处 system .loadlibary
 
              # Sets the library as a shared library.
              SHARED
 
              # Provides a relative path to your source file(s).
              # Associated headers in the same location as their source
              # file are automatically included.
              ${SRC_LIST}
              ${SRC_LIST2}
              ${SRC_LIST3}
              )



####################################################

5.实现.cpp文件里jni方法

 首先,需要弄清楚jni的几个要点:

 1)jni数据类型:

  http://www.cnblogs.com/guanmanman/archive/2017/05/05/6811264.html

  弄清jni有哪些类型,对应c和java分别是什么类型.

  通过查看<jni.h>文件,可以发现最终jni的某种数据类型就对应C中的某个类型.

  注意:一般jni的类型是无法直接被C原生代码调用,需要通过env->相关方法转换一下.

   

 2)jni的方法api:

  所有jni方法的第一个参数:JNIEnv * env

  通过env->方法名()即可调用jni方法.


  数组操作函数

  >>jni数组变换成c可用的数组指针 GetByteArrayElements

   

1
jbyte *olddata1 = env->GetByteArrayElements(jbyteArray1, 0);

   c语言方法参数往往会是一个指针,如果直接将jbyteArray类型转换成指针,会发生错误.所以需要

jni的api将jni类型过渡成原生C可用的类型.

  >> jni数组转数组指针

    jni在调用C源码的时候,很多时候参数都是指针,如果直接传jbyteArray就会报错。需要通过

jni的api将数组转换成指针

1
jbyte *olddata1 = env->GetByteArrayElements(jbyteArray1, 0);

  

 >> 获取数组长度

  

1
jsize oldsize1 = env->GetArrayLength(jbyteArray1)

 这里的jsize即是jint类型。


 >> jni数组截取 

  SetByteArrayRegion(新数组,tart_index,end_index,原始数组指针) 

1
2
3
4
5
//新数组,用于接收截取后的数组
jbyteArray olddata5Resut = env->NewByteArray(len5);
 
jbyte *by5 = (jbyte*)olddata5;
env->SetByteArrayRegion(olddata5Resut, 0, len5, by5);

 3) 

  


eclipse中的CDT插件怎么安装

http://zhidao.baidu.com/link?url=NAsq91axWRclHe2OsTW5nEeVzQG5n6ZVf28h-JP8Tiw75d3YVR18XVunRtv6xsJIAx1-FZrEDNA5an3OIzL6lK


出现的问题:

1.之前写过的一个Android项目上有JNI,后来ADT更新了,出现下面的问题,并且Android没有NDK选项。

[原]cygwin下载、安装教程和解决CDT出现“program 'make' is not found in path”bug


但是上面的这个博客里有一个地方说得不对,就是url连接不一定要使用中国的镜像,中国的镜像有可能

不好使,在列表中随便选一个就行了。


Eclipse CDT plugin problems(StackOverFlow的解答)


eclipse+cygwin+cdt搭建c/c++开发环境

http://www.cnblogs.com/skyofbitbit/p/3705994.html


MinGW 是什么?

http://blog.csdn.net/jpcfei/article/details/6428613

MinGW如何卸载:将文件夹直接删除即可。


最后终于找到答案了:

http://stackoverflow.com/questions/11579135/program-make-not-found-in-path

You may try altering toolchain in case if for some reason you can't use gcc. Open Properties for your project (by right clicking on your project name in the Project Explorer), then C/C++ Build > Tool Chain Editor. You can change the current builder there from GNU Make Builder to CDT Internal Builder or whatever compatible you have.


同时我也将C:\MinGW64\bin下的xx-make32.exe直接改成了make.exe

还修改了3个环境变量

    C_INCLUDE_PATH=C:\MinGW\include

     CPLUS_INCLUDE_PATH=C:\MinGW\include

    LIBRARY_PATH=C:\MinGW\lib

总之,最新的ADT的CDT的配置可能会出现问题,我也不知道具体是哪里出现了问题,反正按照上面的来做,就解决了问题。



 

  1. NDK


    本地开发包 谷歌提供 在谷歌官方文档Develop-Tools-Download-NDK Download目录下有下载链接

    交叉编译:在一种操作系统上编译出来另外一个操作系统上可以执行二进制文件

  

  在Eclipse里关联NDK

  在环境变量Path里配置ndk-build所在路径,以便在编译.c文件时使用这个指令。


wKiom1Xgc2egZ-vzAAFOXL-Jdcc357.jpg


2. JNI开发HelloWorld的复杂写法

  

  (1)定义本地方法  native

      public native String getStringFromC();

  (2)在工程目录下创建一个jni目录

     (2-1)在jni目录下新建.c文件

       方法名的定义 Java_包名_类名_方法名 

       必须接收2个参数 JNIEnv* env jobject obj


   wKiom1XgfYLQEE_cAAFdD-QMKdU016.jpg


    (2-2) 在jni目录下新建Android.mk文件


   wKiom1XggCLhJSQUAAEN59IuP1E761.jpg  

    (2-3) 在jni目录下新建Application.mk文件

        文件内容为APP_ABI :=all。如果没有Application.mk文件,只有Android.mk的话,编译时只能生成arm

    处理器对应的库文件,项目只能运行在arm架构的模拟器上,加了Application.mk文件,就能生成所有类型的处理

    器对应的文件。

  (3)编译.c文件

     在cmd命令行进入项目带盘符路径,输入ndk-build命令(确保Path里已经配置好了),ndk会自动寻找jni目录

     下的文件进行编译,生成os库文件。刷新libs文件夹,查看有没有成功生成os文件。

  (4)引入库文件

      在本地声明的类的静态代码块里

      System.loadlibary("名称");    名称就是Android.mk文件中LOCAL_MODULE那一项对应的名称

  (5)运行项目

      如果在jni下只新建了Android.mk文件,只能在arm架构的模拟器上运行,在其它的模拟器上运行会报错。

      如果想要在其它类型的模拟器上运行,需要Application.mk文件,且要重新编译。


    如果忘记了.mk文件的写法,可以参考ndk的文档:

    wKioL1XginCwgBKBAAHhmYRXi5g511.jpg



3. JNI开发HelloWorld的简单写法 

  (1)先给eclipse配置一些ndk环境  window->preference->Android->ndk-找到你自己ndk所在目录

  (2)定义本地方法

  (3)点击工程右键 添加本地支持 Android Tools-add native support,需要给库文件取个名字。

  (4)自动生成jni目录 自带Android.mk文件 和一个.cpp文件  需要你把cpp改成.c文件 Android.mk文件里面的LOCAL_SRC_FILES也需要改一下

  (5)使用javah  生成jni样式头文件

      

  (6)在C代码中实现本地方法 

  (7)一定要记得给他一锤子 (编译)

  (8)在声明本地方法的类中 在静态代码块中加载库文件



Java调C


 

C调Java





#########Android Studio配置NDK##########

 http://blog.csdn.net/aplixy/article/details/51429305 (****)

 http://www.cnblogs.com/Andrew-XinFei/p/5608001.html

 http://blog.csdn.net/a_zhon/article/details/53097512


  1. 经常出现的一个错误 http://blog.csdn.net/commshare/article/details/53186025 

 2.android studio与eclipse不同,不需要手动编写mk文件,只要在gradle里将ndk及其module配置好

 。

3.android studio .c和.h文字报红,如果项目能run起来的话,不用管,这不是bug.



#########app应用卸载监听##########

  1.  http://www.apkbus.com/thread-250628-1-1.html

2.http://blog.csdn.net/delmoremiao/article/details/52032832 (实现思路)







      本文转自屠夫章哥  51CTO博客,原文链接:http://blog.51cto.com/4259297/1689141,如需转载请自行联系原作者



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值