传统的关于android使用JNI调用C/C++程序,首先javah 生产头文件,然后拷贝头文件里面的方法到C文件中进行映射调用,由于这种方法生成的映射方法名不太规则也比较长,二呢是调用数据较慢;因此可以使用JNI动态注册方法的方式来解决这2问题。
** 1 在下面根目录下新建jni文件夹随便新建文件xxx.c;并且拷贝一份 Android.mk文件到jni下面,如:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS :=-llog
LOCAL_PROGUARD_ENABLED:= disabled
LOCAL_MODULE := nativenck #生产模块名称
LOCAL_SRC_FILES := com_exmple_ndk_NativeNCK.c#模块名称
include $(BUILD_SHARED_LIBRARY)
** 2 新建native的class,如NativeNCK:
public class NativeNCK {
static{
System.loadLibrary("nativenck");
}
public static native String getUrl();
public static native void setData(byte str[],int type);
}
以上有2个方法需要调用C/C++层代码,方法名自定。
** 3 在xxx.c文件中首先导入常用包以及定义log:
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <Android/log.h>
#define TAG "result" // 这个是自定义的LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)
** 4 添加JNINativeMethod方法:
static const JNINativeMethod gMethods[] = {
{"getUrl", "()Ljava/lang/String;", (jstring*)getUrl},
{"setData","([BI)V",(void*)setData}
};
ps:{“java代码中的方法名”,”方法([BI)V-表示传了byte数组和一个数字,并且为void方法;关于参数类型详细请看4-1”,
“这里是对C文件中的方法名写法:(返回类型*)+方法名”}
中间那个参数就是用来确定是否java端有传值过来,并且传值有规范写法如void function(Sting str,int a)方法在c中可以这么写: {“function”,”(Ljava/lang/String;I)”,”(void*)functionToJava”}
4-1图:
** 5 c文件中编写本地方法即可,如:
static jstring JNICALL getUrl(JNIEnv *env, jobject jobj){
return (*env)->NewStringUTF(env,"hello!Im C!");
}
static JNICALL setData(JNIEnv *env, jobject jobj,jbyteArray zstr,jint zint){
LOGI("----setData----");
const char* str = (char*)zstr;
LOGI("----INT=%d",zint);
}
** 6 最后一步在JNI_Onload方法中注册,如:
static jclass myClass;
#这里是java调用C的存在Native方法的类路径
static const char* const kClassName="com/exmple/ndk/NativeNCK";
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
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;
myClass = (*env)->FindClass(env, kClassName);
if(myClass == NULL)
{
printf("cannot get class:%s\n", kClassName);
return -1;
}
if((*env)->RegisterNatives(env,myClass,gMethods,sizeof(gMethods)/sizeof(gMethods[0]))<0)
{
printf("register native method failed!\n");
return -1;
}
printf("--------JNI_OnLoad-----");
return JNI_VERSION_1_4; //这里很重要,必须返回版本,否则加载会失败。
}
至此即可动态的注册方法到C中,不需要每次java层添加了新方法需要重新编译一次头文件。
ps:注意 注意 注意 …. 使用JNI动态注册的本地方法参数必带【JNIEnv *env,jobject *obj】否则会导致参数在C/C++中取值不正确。如:
void test(JNIEnv *env,jobject *obj,int arg1, int arg2,int arg3);
如果这里没写jobject *obj,导致本地拿到的值arg1没有,arg2赋值是arg1原来的值,arg3赋值是arg2原来的值。