Android JNI 动态注册方法(JNI_OnLoad)

传统的关于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图:

Android JNI 动态注册方法(JNI_OnLoad)_第1张图片Android JNI 动态注册方法(JNI_OnLoad)_第2张图片

** 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原来的值。

你可能感兴趣的:(Android)