您的位置:首页 > 移动开发 > Android开发

Android JNI 动态注册方法(JNI_OnLoad)

2016-09-06 10:44 971 查看
传统的关于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原来的值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android jni