Android JNI 动态注册方法(JNI_OnLoad)

来源:互联网 发布:清真食品卫生吗 知乎 编辑:程序博客网 时间:2024/05/18 16:35
传统的关于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 :=-llogLOCAL_PROGUARD_ENABLED:= disabledLOCAL_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原来的值。

0 0
原创粉丝点击