android ndk的使用 jni
最近android项目中底层加密方法需要用c实现,这样调用就需要用到jni了,以下是我学习的一点心得,写出来分享一下:
1、准备工具
需要Android NDK和windows下c、c++代码的编译器cygwin
android-ndk-r9-windows.zip下载地址:http://developer.android.com/tools/sdk/ndk/index.html
cygwin下载地址:http://u.115.com/file/clvy46eb
2、环境配置
1. 解压缩NDK和cygwin,并配置cygwin的环境变量。
例如:我解压缩到D盘根目录,那么在环境变量的path中添加:”D:\cytwin\bin;”,
如下图所示。
2. 在命令行中进入cygwin目录,并执行cygwin.bat,如果你不是用Administrator账号登录的系统,那么会在cygwin\home\文件夹中生成一个以你的登录名命名的新的文件夹。
3. 修改新生成文件夹中的“.bash_profile ”文件,用UE或editplus等文本编辑器打开,在最后增加:
NDK=/cygdrive/d/android-ndk-r6
export NDK
然后保存关闭。
d/android-ndk-r6表示ndk在d盘,如果你的ndk放到了其他盘符或路径,例如放到e:\sdk目录中,那么就改为e/sdk/android-ndk-r6。
(如果是Administrator账号登录,直接修改Administrator文件夹中的文件。)
四.开始使用
1.在命令行窗口中进入cygwin目录,执行cygwin.bat
2.进入android-ndk中的samples文件夹里的某个项目文件夹,这里以hello-jni为例
进入文件夹的方式与MS-DOS类似,用cd + 空格 + 盘符+ 路径即可,例子见下图
3.执行$NDK/ndk-build编译
4.例子编译成功后得到libhello-jni.so文件,到此环境就配置好了
五、在自己的项目中使用jni
1、首先写java调用的native方法,示例代码如下:
package com.magic.point;
public class EncryptJNI {
static {
System.loadLibrary("magic-point-jni");
}
public static native void init();
public static native String getHiddenString(int index);
}
2、在jni中c的方法名名称需要是固定格式,我们可以javac EncryptJNI 编译java文件,然后用
javah -jni com.magic.point.EncryptJNI 编译生成com_magic_point_EncryptJNI.h头文件,格式如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_magic_point_EncryptJNI */
#ifndef _Included_com_magic_point_EncryptJNI
#define _Included_com_magic_point_EncryptJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_magic_point_EncryptJNI
* Method: init
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_magic_point_EncryptJNI_init
(JNIEnv *, jclass);
/*
* Class: com_magic_point_EncryptJNI
* Method: getHiddenString
* Signature: (I)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_magic_point_EncryptJNI_getHiddenString
(JNIEnv *, jclass, jint);
#ifdef __cplusplus
}
#endif
#endif
我们可以看到命名格式如下:
JNIEXPORT jstring JNICALL Java_全包名_类名_getHiddenString()
3、接下来我们就可以新建一个Hellojni.c文件,导入com_magic_point_EncryptJNI.h头文件,在c里面具体实现头文件中的方法,完成后将com_magic_point_EncryptJNI.h和Hellojni.c文件复制到android-ndk-r9/samples/hello-jin/ 的jni目录下,修改mk配置文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Hello-jin //编译生成的so文件名称
LOCAL_SRC_FILES := Hellojin.c //需编译的文件
include $(BUILD_SHARED_LIBRARY)
执行$NDK/ndk-build编译将生成libHello-jni.so文件,将libHello-jni.so文件复制到android工程libs目录下,调用String result=EncryptJNI.getHiddenString(0); 即可。
六,jni中的类型转换
在jni中的数据类型和java中可直接使用的类型不用,需要转换一下,以下列出char 转jstring类型和jstring类型转char的方法
1、char 转 jstring
const char * name;
jstring jstr = (*env)->NewStringUTF(env,name);
2、jstring 转 char
char* jstringToChar(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = (*env)->FindClass(env,"java/lang/String");
jstring strencode = (*env)->NewStringUTF(env,"utf-8");
jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr, mid, strencode);
jsize alen = (*env)->GetArrayLength(env,barr);
jbyte* ba = (*env)->GetByteArrayElements(env,barr, JNI_FALSE);
if (alen > 0)
{
rtn = (char*)malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
(*env)->ReleaseByteArrayElements(env,barr, ba, 0);
return rtn;
}