JNI 开发步骤:
声明所需要的native 方法。
根据需要,在java代码中,声明所需要的native本地方法,和方法要实现的目的。
/**
* 声明一个本地方法,
* 调用C代码获得字符串信息,并返回
* @return
*/
public native String getmessage();
在C代码中实现对应的native 方法
- 在项目中新建jni文件夹 在jni文件夹中新建一个c文件,如hello.c
- 在hello.c文件中,声明与native 方法对应的c的方法
* 参数 ndk目录\platforms\android-16\arch-arm\usr\include\jni.h 文件
/**
* 返回值 jstring 是C中定义的与java中的String类型匹配的数据类型
* 方法名:
* Java_包名_类名_方法名
* 参数一:JNIEnv* env java虚拟机运行jni的环境
* 参数二: jobject jobj 是调用当前方法的java对象的引用
*/
jstring Java_com_example_jnihello1_MainActivity_getmessage(JNIEnv* env,jobject jobj)
- 实现该方法,同时参考 JNINativeInterface 结构体中,返回对应的数据类型
char * pstr = "hello i am from c";
// 在 JNINativeInterface 结构中 查找到该方法
// jstring (*NewStringUTF)(JNIEnv*, const char*);
return (*env)->NewStringUTF(env,pstr);
- ** 注意在C文件中 #include <jni.h> **
将C代码编译为linux 环境下可运行的二进制文件
- 在jni目录中新建 Android.mk 配置文件
- 参照 ndk目录/docs/ANDROID-MK.html 编写该文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello // 自己定义的当前模块的名称
LOCAL_SRC_FILES := hello.c // 该模块需要的C文件
include $(BUILD_SHARED_LIBRARY)
- 在文件游览器中打开jni文件夹,shift+右键,打开当前目录的cmd命令行。
执行 ndk-build 命令
* 注意 ndk的目录要事先加入 path 环境变量当中
- 编译成功后,会在android 项目中生成 libs/armeabi/libhello.so 文件
在java 代码中,加载 so 文件
习惯上,都是在静态代码块中加载
static{
// 加载编译好的 so 文件
System.loadLibrary("hello"); // 注意 名称是libhello.so 去掉前三个和后三个字符省下的。其实就是Android.mk 文件中配置的模块名
}
JNI开发中的常见问题
Caused by: java.lang.UnsatisfiedLinkError: Couldn’t load hello: findLibrary returned null
加载so库时出错,说找不到对应的库文件。
出现错误的原因:
一:可能是so文件的名称写错了
注意:加载的文件名,出Android.mk 文件中声明的模块名要一致二:没有对应平台的so 文件
编译出来的so文件是arm平台可用的,而模拟器是inter的模拟器
解决方法:编译出支持inter CPU的so 文件Caused by: java.lang.UnsatisfiedLinkError: getmessage
调用native方法时,报此错误,是因为没有找到 与native 对应的C的函数
出错的原因:
极有可能是 C文件中 函数名没有写对。检查C函数名是否正确
* 方法名:
* Java_包名类名方法名
有参数的JNI 调用
- 如果native 方法自身包含参数 那么对应的C的方法,也要有相应的参数
如native方法: public native int jniAdd(int m,int n);
那么对应的C的方法:
/**
- 返回值为 jint 类型
* - 方向法:Java_包名类名方向名
- 方法的参数,前二个是固定的 JNIEnv* 是jni的运行环境 jobject 是调用方法的java对象
- 后面二个参数是 java代码中,native方法的真实的参数
*/
jint Java_zz_itcast_jnihello2_MainActivity_jniAdd(JNIEnv* env,jobject obj,jint m,jint n){
- 返回值为 jint 类型
javaH命令的使用
- 打开项目bin/classes 目录,在此目录中,shift+ 右键 打开命令行,
执行命令 javah zz.itcast.jnihello2.MainActivity
会生成相应的头文件,该文件中就包含了和native方法对应的C的方法的声明,将方法的声明复制到C代码中,改为正确的方法即可。
* 注意:上面的方法,在java1.6中有效 在java1.7中执行javaH 命令是在 src目录中而不是bin目录 *
NDK
NDK ( native develop kit ) 本地开发工具集,
是谷歌公司提供的,可以winodws平台,模拟出不同的其他平台的开发环境。
CDT 在eclipse中开发C语言的一个插件。
DNK 目录结构:
– build 编译代码时,所需要的工具脚本
* .sh sh扩展名是linux 下的可执行脚本,类似于windows中的bat 批处理命令
– docs ndk的说明文档
– platforms 包含不同的android版本,不同的CPU编译时,所需要的文件
– prebuilt 预编译,编译之前进行的准备,和测试的工具
– samples ndk自带的一些小例子
– sources 源码
– tests ndk的一些测试用例
– toolschains 编译的时候,需要的工具连
ndk 根目录下: ndk-build.cmd 编译时,执行的widnows命令。
* ndk解压后的目录,必须添加至系统path 环境变量 *
使用NDK集成开发环境的步骤:
在eclipse中配置ndk的安装目录
在 window/preferences/Android/NDK 中设置好NDK的解压目录
一:声明所需要的native 方法。
根据需要,在java代码中,声明所需要的native本地方法,和方法要实现的目的。
/**
* 声明一个本地方法,
* 调用C代码获得字符串信息,并返回
* @return
*/
public native String getmessage();
二:集成NDK开发环境
> 选中项目-右键 -- Android Tools -- add Native Support
如果打开的面板出错,是因为没有配置ndk目录
在打开的面板中输入 Android.mk文件的模块名,该模块名,也是在java代码中加载so库文件的名称
* NDK 环境默认创建的是cpp文件,即c++文件,目前先把 c++文件,都改为c文件
> 使用javah 命令生成对native 方法对应的C函数的声明,并复制至c文件中
> 此时,文件会报错,是因为没有关联C源码。
关联C代码:
选中项目,右键属性-- c/c++ general -- path and synbol 设定C源文件的路径:
如: D:\android-ndk-r9b\platforms\android-16\arch-arm\usr\include
三:实现C代码,并返回相应的数据
四:在java代码中加载so库文件
直接运行该应用,ndk会自动将C代码编译成so文件。