JNI详解以及使用Java调c代码

来源:互联网 发布:南京大学cssci数据库 编辑:程序博客网 时间:2024/05/19 01:10

**JNI详解**

什么是JNI

JNI Java Native interface 一种协议 提供一套编程框架,java和本地代码相互调用

为什么需要JNI

1.操作底层硬件,Android平台上传感器

2.应用程序对运行效率有要求,图形渲染,音视频解码

3.复用成熟C开源软件,OpenGL,OpenSSL,SQLite3,FFmpeg

4.安全性要求,本地C代码反编译困难

5.复用公司之前成熟代码,跑跑卡丁车

C基本数据类型 c里面char占1byte,而java占2bytes;C里面的long占4bytes ,java中占8bytes c里面没有boolean类型,可以用unsigned char代替 //signed unsigned ,整数类型前缺省表示signedC语言输入输出函数 都需要包含头文件stdio.h d u o x 整数,c s,fC语言格式化输出都需要用到占位符,常用的占位符如下: %d 十进制有符号 int %u 十进制无符号 unsigned int %o 八进制无符号 unsigned int %x 十六进制无符号 unsigned int 在%o和%x中间加#,输出前导0,0x %c - char 表示字符 char 类型如果以%d输出会打印当前字符的ASII值 %c 输出一个字符 %s 输出一个字符串,若干个 若要输出long和double类型,在相应字符前l %ld %lf 输出short类型用 %hd %p输出变量的地址 格式化输入函数scanf d c s f 输入其它数据 printf(“请输入整数:”); scanf(“%d”,&i);//&i告诉scanf输入数据存到哪 输入字符串写法 char buf[10];//字符数组,可以保存字符串 ‘\0’作为字符串的结束标志 printf(“输入字符串:”); scanf(“%s”,buf);//数组名代表数组首元素首地址 buf[0] &buf[0]

什么是指针

1.内存:操作系统管理的一类存储资源.

最小单位1byte,8bits,一维线性分布 32cpu 4G内存的地址空间 1k = 1024bytes 2^10 1M = 1024k = 1024 * 1024 bytess 2^20 1G = 1024M = 1024 * 1024 * 1024 bytes 2^30 4G = 4 * 1G = 2^2 * 2^30 = 2^32

2.内存单元:通过内存编号来区分管理,内存单元的编号可以作为内存单元的地址

3.指针:在C中把内存单元的编号称指针

指针变量 普通变量:一般数据 指针变量:保存内存单元地址,可以利用指针运算符*访问内存的三种应用含义 ## 3 * 5 : *乘法 int *p; : *定义指针变量,*区分了变量类型 *p = i; *取对象运算符指针的指向 :指向:如果一个指针变量保存了一个对应类型变量的首地址,指针变量指向这个变量,可以用指针运算符*访问指向变量。 int i = 20; char c = ‘k’; int *p; char *q; p = &i;//指针变量p指向i q = &c; //p = &c; //p = (int *)&c;//如果真要保存需要强转 (要转的指针类型) //*p

数组的特点:

1.元素数据类型相同

2.元素在内存中连续的

3.数组名代表首元素首地址 arr <=> &arr[0]

指针与数组的关系 a[i]

函数指针

函数指针:函数在代码段起始地址,函数入口地址

函数指针变量:函数的入口地址

函数名代表函数的入口地址

void funA();//函数声明int funB(int *a,int n);//函数声明void (*pfA)();//函数指针变量,可以保存funA地址int (*pfB)(int *,int);//函数指针变量的定义,可以保存funB函数的入口地址

结构体与联合体

typedef关键字

typedef 作用是给己经存在的数据类型起别名

int a;//a是变量名,是一个标识符ypedef int a;//typedef 让a成为与int 完全等价的类型名a b;//用类型别名定义变量int c;//用原类型名定义量a <=> intstruct Student *pstu;//pstu是一个结构体指针变量,是一个标识符typedef struct Student *pstu;//typedef 让pstu成为了与struct Student*等价的类型别名pstu k;struct Student *m;

相关基本概念

交叉编译:在一个平台上为另一个平台编译程序;不同的操作系统,windows Linux;不同的处理器架构Intel ARM

Host:编译程序的平台,主机Target:运行程序的平台,目标机交叉编译工具链:编译套件提供一系列,顺序调用,形成链条,arm-linux-gcc

使用工具

  • NDK: Native Development kit
  • CDT: C/C++ Development tools 本质上是一个eclipse插件
  • Cygwin:在windows提供了一个unix模拟运行环境

NDK目录介绍

  • docs 开发文档 推荐D:/android-ndk-r9d/documentation.html
  • build *.mk 指导编译的配置文件
  • platforms 系统头文件D:\android-ndk-r9d\platforms\android-18\arch-arm\usr\include
  • prebuild 编译工具 make.exe 工程管理器
  • samples 开发例子
  • source 相关库的源码
  • toolchains 交叉编译工具链
  • ndk-build.cmd 谷歌提供的工程编译命令

    JNI开发流程

阶段 编码

1.创建Android工程,用native声明本地方法

    public native String helloFromC();

2.在工程新建jni,其中新建.c源文件

        #include <jni.h>        /** jni规定 本地方法名 Java_调用本地方法类所在的包名_类名_方法名         *  JNIEnv * env    java环境,提供函数供调用         *  jobject obj     调用本地方法的对象         *         *  typedef const struct JNINativeInterface* JNIEnv;         *  JNIEnv <=> struct JNINativeInterface*         *  env : JNIEnv * <=> struct JNINativeInterface**         *  (*env)->NewStringUTF();         */         jstring Java_com_itheima_helloworld_MainActivity_helloFromC(JNIEnv *env, jobject obj){              //jstring     (*NewStringUTF)(JNIEnv*, const char*); 把C字符串转化为java中字符串        // 把C字符串转化为java中字符串        return (*env)->NewStringUTF(env,"hello world");    }

阶段2 编译

3.在jni目录下新建Android.mk文件

     LOCAL_PATH := $(call my-dir)  #提定当前路径     include $(CLEAR_VARS)         #清除全局配置变量,LOCAL_XXX,除了LOCAL_PATH     LOCAL_MODULE    := hello       #指定生成动态库名hello,生成的动态库文件libhello.so     LOCAL_SRC_FILES := hello.c     #指定生成动态库的源文件     include $(BUILD_SHARED_LIBRARY) #提定生成动态库

4.在jni目录下用ndk-build命令编译生成.so动态库(把ndk-build工具路径添加到path)

5.在类中加载动态库

     static{            System.loadLibrary("hello");//加载动态库名而不动态库文件名        }

jni开发过程中常见错误

找不到动态库: Couldn't load resultfr0mc: findLibrary returned null可能原因库名写错;或者生成动态库与运行平台不符合
找不到方法: Caused by: java.lang.UnsatisfiedLinkError: Native method not found: com.itheima.resultfromc.MainActivity.resultFromC:(II)I 解决方法检查JNI函数名, javah命令使用 jdk1.6在工程的bin\classes目录下执行,生成头文件

javah com.itheima.resultfromc.MainActivity
jdk1.7在工程的src目录下执行,生成头文件
javah com.itheima.resultfromc.MainActivity
结合eclipse和NDK的JNI开发流程

1.新建Android工程,声明本地方法

2.指定工程添加 add Native suport,修改其中源文件后缀为.c,在Android.mk文件中也要修改,见gif图片add ndk location2.gif(ndk的支持)

3.指定jni.h头文件路径见gif图片include jni head file.gif

4.进入工程执行javah命令 ,把jni函数声明拷贝到.c源文件中

5.实现本地jni函数(删除错误提示)

6.在调用类中加载动态库 System.loadLibrary(“test1”);

需要在所有平台上跑需要创建:Application.mk文件,写: APP_ABI := all

C调用java中方法的步骤

//1.装载字节码//jclass      (*FindClass)(JNIEnv*, const char*);//所在的包名jclass clazz = (*env)->FindClass(env,"com/itheima/alipay01/MainActivity");//2.get method id                     字节码    方法名       方法的签名    //jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);                    bin/classes,执行javap获取签名jmethodID methodid = (*env)->GetMethodID(env, clazz, "showDialog", "(Ljava/lang/String;)V");//3.call void  methoid//void       (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);(*env)->CallVoidMethod(env, obj, methodid, "yong hu mi ma");

jni开发三种情况

1.只有java工程师
2.只有java工程师,但还有.so动态库
3.有java工程师,c工程师;处理方式
* 1.把C程序员写的头文件和.c源文件拷贝工程中jni目录下
* 2.在jni.c文件中包含头文件,再调用其中的函数
* 3.在Android.mk文件中添加用到的.c文件,指定编译进入最终.so动态库
LOCAL_SRC_FILES := smarthome-jni.c get_house_temp.c

C和C++编写jni代码区别

1.源文件后缀不同

2.调用JNIEnv中函数方式不同

    //在C中调用    (*env)->NewStringUTF(env,"Hello from C")    //在C++中调用    env->NewStringUTF("Hello form Cpp")

3.c++代码需要在jni源文件中包含由javah生成的头文件

    #include <jni.h>    #include "com_itheima_hellofromcpp_MainActivity.h"  
2 0
原创粉丝点击