网上的很多教程讲的NDK开发都是用C语言来做的,但是又有很多代码是用C++写的.虽然说可以使用C调用C++,但是C调用C++的难度比C++调用C的难度大.原因C++容易兼容C,但反之需要复杂的操作,特别是调用成员函数的操作.
我们都知道JNI支持C++开发,java的本地代码调用:
JNIEXPORT 和 JNICALL 这两个宏确保函数在本地库外可见,又根据函数的命名和java 本地方法是一一对应的.
这样就可以根据方法名直接找到该函数.但是据我所知,只适合C语言,或者extern "C",但是一旦使用了这些,又会
增加C调用C++的复杂度.说的有错误请指正.
但是在使用jni开发中,JNIEXPORT 和 JNICALL 不是必须.我们可以完全可以手动进行本地方法的注册.
我们会用到jni中的RegisterNatives函数对本地方法进行注册.以下是例子重要代码.
//jni.cpp
#include <jni.h>
#include<Stdio.h>
//如果#include <iostream>
// 需在 在Application.mk 添加 APP_STL := gnustl_static
#include "Math.h"
#include "ALOG.h"
jint Java_com_example_ndkwithcpp_Tools_add(JNIEnv *env,
jobject obj, jint a, jint b);
static JNINativeMethod methods[] = { { "add", "(II)I",
(void*) Java_com_example_ndkwithcpp_Tools_add } };
static const char *classPathName = "com/example/ndkwithcpp/Tools";
//可以对同一个java class的所有本地方法一次性注册.
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;
}
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
jint result = -1;
JNIEnv* env = NULL;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
goto bail;
}
if (registerNativeMethods(env, classPathName, methods,
sizeof(methods) / sizeof(methods[0])) != JNI_TRUE) {
goto bail;
}
result = JNI_VERSION_1_4;
bail:
LOGI("JNI_ONload code: '%d' ", result);
LOGI("版权所有,http://xxx.com/xxx\n");
return result;
}
jint Java_com_example_ndkwithcpp_Tools_add(JNIEnv *env, jobject obj, jint a,
jint b) {
Math math;
int c = math.add(a,b);
return c;
}
需要注意的是,使用C++和C调用jni 函数的方式是不同的.如c这样调用 (*env)->fun(env,参数1,参数2,..)
而C++确是这样调用的,env->fun(参数1,参数2,...);
而对于vm也是一样的.C的是(*vm)->fun(vm,参数1,参数2,...);
//ALOG.h
extern "C" int __android_log_print(int prio, const char *tag, const char *fmt,
...);
//prio取值如下
typedef enum android_LogPriority {
ANDROID_LOG_UNKNOWN = 0,
ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
ANDROID_LOG_VERBOSE,
ANDROID_LOG_DEBUG,
ANDROID_LOG_INFO,
ANDROID_LOG_WARN,
ANDROID_LOG_ERROR,
ANDROID_LOG_FATAL,
ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
} android_LogPriority;
//tag为标签。
#ifndef LOG_TAG
#define LOG_TAG "DEBUG"
#endif
#define LOGI(fmt,...)\
__android_log_print(ANDROID_LOG_INFO,LOG_TAG, \
"at %s(%d)-%s:\n" fmt "\n", __FILE__, __LINE__, __FUNCTION__,##__VA_ARGS__)
而对于LOG,注意要先声明 extern "C" int __android_log_print(int prio, const char *tag, const char *fmt,...); 再定义宏.这里的LOGI函数加入了__FILE__, __LINE__, __FUNCTION__宏,可以在log中快速找到LOGI的位置.如 at jni/jni.cpp(48)-JNI_OnLoad:
宏定义的\表示将一行分成多行,但是\后面不许接任何字符,包括空格,如果代码预览不正常,自己改正.
//Math.h
class Math {
public:
int add(int a, int b);
};
//Math.cpp
#include"Math.h"
int Math::add(int a, int b) {
return a + b;
}
//Android.mk
LOCAL_PATH := $(call my-dir)
LOCAL_CPP_EXTENSION := .cpp
include $(CLEAR_VARS)
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
LOCAL_MODULE := jni
LOCAL_SRC_FILES := LOCAL_SRC_FILES := Math.cpp jni.cpp
include $(BUILD_SHARED_LIBRARY)
第二句LOCAL_CPP_EXTENSION,扩展名为cpp
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
使用了这句,就不用再使用#include <utils/Log.h>了.
LOCAL_SRC_FILES:=为要编译的源文件,包括include进去的头文件的函数实现.如Math.cpp
//Application.mk
APP_ABI := all
//Tools.java
package com.example.ndkwithcpp;
public class Tools {
public native int add(int a, int b);
static {
System.loadLibrary("jni");
}
}
至于如何使用NDK工具编译代码,自己找教程吧.