android JNI学习三

来源:互联网 发布:淘宝商城手机壳 编辑:程序博客网 时间:2024/06/05 09:58

先看一个参考说明:Android JNI(实现自己的JNI_OnLoad函数) http://blog.csdn.net/zhenyongyuan123/article/details/5862054

这次尝试使用 env->RegisterNatives 的方法向系统注册 jni 的接口。向系统注册,还可以使用 AndroidRuntime::registerNativeMethods 这个方法,要详细内容可自己百度。

jni_call.cpp文件修改如下:

#include "tools.h"
#include <jni.h>
#include "JNIHelp.h"
#include <android/log.h>
#define JNIREG_CLASS "com/example/jnicall/MainActivity"//指定要注册的类
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "jni_call", __VA_ARGS__)

#ifdef __cplusplus
extern "C" {
#endif
jint Java_com_example_jnicall_MainActivity_add(JNIEnv* env, jobject thiz, jint add1, jint add2){

LOGD("add = %d",add(add1, add2));
    return add(add1, add2);
}
#ifdef __cplusplus
} // extern "C"
#endif

JNIEXPORT void JNICALL jni_getImage(JNIEnv* env, jclass jclazz)
{
LOGD("jni_getImage");
  return ;
}

static JNINativeMethod gMethods[] = {
{ "_getImage", "()V", (void*) jni_getImage }
};

static int registerNativeMethods(JNIEnv* env, const char* className,
        JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}

static int registerNatives(JNIEnv* env)
{
if ( !registerNativeMethods(env, JNIREG_CLASS, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) )
{
return JNI_FALSE;
}
return JNI_TRUE;
}

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv *env;
if ( (vm->GetEnv( (void**) &env, JNI_VERSION_1_4)) != JNI_OK) {
return -1;
}
if ( !registerNatives(env) ) { //注册
return -1;
}
    return JNI_VERSION_1_4;
}

改动比较大,增加了 log 的输出,#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "jni_call", __VA_ARGS__) 是 jni 曾增加 log 的定义宏,要加头文件 <android/log.h> ,而且在 mk 文件里也要添加编译库,下面会附上mk文件内容。

在 JNI_OnLoad 增加了注册函数 registerNatives(env) ,有调用 registerNativeMethods 函数,最终调用 env->RegisterNatives(clazz, gMethods, numMethods) 这个函数,主要是里面传递的第二个参数 gMethods :

static JNINativeMethod gMethods[] = {
{ "_getImage", "()V", (void*) jni_getImage }
};

这个 JNINativeMethod 数据结构体就是把 java 的 native 方法映射成 C++ 函数。(个人观点)

然后把结构体向系统注册后,java 调用 native 方法时,就是调用 C++ 函数了。JNINativeMethod 的命名规范可上网查,简单来说,第一项是 java 里定义的 native 函数名,个人习惯用 _  开头。第二项是表示函数的返回值和参数,()V 表示无返回无参数,括号内是参数,这里是空的,V 表示无返回值。第三项就是对应 C++ 函数名了。前面一定要加 (void*) ,无论是否带返回值。

注意:这里函数是有顺序的,否则编译会不通过,也就是说,在 JNI_OnLoad 之前,要先写 static int registerNatives(JNIEnv* env),不然编译的错误就是:

error: 'registerNatives' was not declared in this scope

我曾经就犯过这种错误,写出来也算是提醒自己不要再犯同样的错误吧。

apk 的 MainActivity.java 修改如下:

package com.example.jnicall;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
add(1,2);
_getImage();
}

static {
System.load("/system/jni/libjnicall.so");
}
public native int add(int add1, int add2);
public native void _getImage();
}

Andrfoid.mk 文件修改如下:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libtools
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := tools.cpp
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_LDLIBS    := -lm -llog
LOCAL_MODULE := libjnicall
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := jni_call.cpp
LOCAL_STATIC_LIBRARIES := libtools liblog
LOCAL_MODULE_PATH := $(LOCAL_PATH)/out/lib
include $(BUILD_SHARED_LIBRARY)

编译运行 apk,打印的 log 如下:

D/jni_call( 4715): add = 3
D/jni_call( 4715): jni_getImage

就说明 RegisterNatives 注册 jni 函数正常可使用了。

这种注册的方法对比前面固定包名的方法主要有点就是修改 C++ 函数会改动较少,网上说这种运行更有效率,我个人未必认同。但 android 里使用注册这种方法更多倒是对的。


代码文件:http://download.csdn.net/detail/u013820413/6996453

0 0