书写Java native接口
知识点:
javah -classpath . -jni com.test.jni.FileJNILib
将Java的String转为C的字符串,字符串的转换如下:
const charconst *p_passwd = (*env)->GetStringUTFChars(env, j_passwd, NULL);
c 代码中声明字符串数组,调用NewStringUTF,即可转为Java String
char szName[256];
(*env)->NewStringUTF(env,szName);
下面我们进入实战的阶段,写代码。
建立一个android1.5的项目,NDK不支持1.5以下的版本
JNI.java的代码:
JniTest.java的代码:
4. 生成JNI 头文件
下面生成JNI头文件啦,使用javah命令,对JNI..java这个文件生成,别搞错啦!
使用cmd进入Android项目的bin目录中,然后执行 javah -classpath . -jni cc.androidos.jni.JNI这个命令。别忘记包名!(^_^)
生成了一个cc_androidos_jni_JNI.h
打开看看:
里面都是c语言的头文件声明
5. 书写C代码,写入数据
下面进行c语言代码的阶段,不会C的,可以去学习一下。
我这里使用eclipse for c/c++的进行开发,简单的代码大家可以使用记事本。
工程如下:
cc_androidos_jni_JNI.h内容不变
cc_androidos_jni_JNI.c的内容:
在这里的代码会在运行的时候向sdcard写入androidos.cc.txt文件。然后写入aaaa数据。
6. 编译SO文件
进行编译so文件:
在NDK的apps目录下面创建一个myjni文件夹,在myjni文件夹中创建一个Application.mk文件,文件的内容为:
在NDKsources目录下创建myjni文件夹,在myjni文件中创建一个Android.mk文件,然后将cc_androidos_jni_JNI.h和cc_androidos_jni_JNI.c文件copy进来:
Android.mk中的内容如下: LOCAL_SRC_FILES := com_test_jni_FileJNILib.c common.c
里面内容的关键字含义请自行查看docs\OVERVIEW.TXT文件
编译:
7. 加入到Android项目中进行运行
将apps\myjni\libs整个文件夹copy到android项目下:
运行android项目使用adb shell进行查看:
进入sdcard查看数据。
注意:sdcard具有system的权限,如果你的目录是root权限的,那么是不能写成功的,除非是真机越权,模拟器是不成功的,并且在Eclipse logcat下会有DEBUG信息出现。
NOT FOUND JNI_OnLoad问题:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
#include
#include
#include "com_test_jni_FileJNILib.h"
/* Header for class com_test_jni_FileJNILib */
/*
* Class: com_test_jni_FileJNILib
* Method: fileCrypt
* Signature: (Ljava/lang/String;Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_test_jni_FileJNILib_fileCrypt__Ljava_lang_String_2Ljava_lang_String_2
(JNIEnv * env, jclass thiz, jstring source, jstring dest){
return 1;
}
/*
* Class: com_test_jni_FileJNILib
* Method: fileCrypt
* Signature: (Ljava/io/File;)Ljava/io/File;
*/
JNIEXPORT jobject JNICALL Java_com_test_jni_FileJNILib_fileCrypt__Ljava_io_File_2
(JNIEnv * env, jclass thiz, jobject source){
return NULL;
}
static const JNINativeMethod gMethods[] = { //定义批量注册的数组,是注册的关键部分
{"fileCrypt", "(Ljava/lang/String;Ljava/lang/String;)I", (void*)Java_com_test_jni_FileJNILib_fileCrypt__Ljava_lang_String_2Ljava_lang_String_2} ,// func2是在java中声明的native函数名,"()V"是函数的签名,可以通过javah获取。
{"fileCrypt", "(Ljava/io/File;)Ljava/io/File;", (void*)Java_com_test_jni_FileJNILib_fileCrypt__Ljava_io_File_2}
};
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void *reserved) //这是JNI_OnLoad的声明,必须按照这样的方式声明
{
JNIEnv* env = NULL; //注册时在JNIEnv中实现的,所以必须首先获取它
jint result = -1;
if((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK) //从JavaVM获取JNIEnv,一般使用1.4的版本
return -1;
jclass clazz;
static const char* const kClassName="com/test/jni/FileJNILib";
clazz = (*env)->FindClass(env, kClassName); //这里可以找到要注册的类,前提是这个类已经加载到java虚拟机中。 这里说明,动态库和有native方法的类之间,没有任何对应关系。
if(clazz == NULL)
{
printf("cannot get class:%s\n", kClassName);
return -1;
}
if((*env)->RegisterNatives(env, clazz,gMethods, sizeof(gMethods)/sizeof(gMethods[0]))!= JNI_OK) //这里就是关键了,把本地函数和一个java类方法关联起来。不管之前是否关联过,一律把之前的替换掉!
{
printf("register native method failed!\n");
return -1;
}
return JNI_VERSION_1_4; //这里很重要,必须返回版本,否则加载会失败。
}