網上的不少教程講的NDK開發都是用C語言來作的,可是又有不少代碼是用C++寫的.雖說可使用C調用C++,可是C調用C++的難度比C++調用C的難度大.緣由C++容易兼容C,但反之須要複雜的操做,特別是調用成員函數的操做. java
咱們都知道JNI支持C++開發,java的本地代碼調用: android
JNIEXPORT 和 JNICALL 這兩個宏確保函數在本地庫外可見,又根據函數的命名和java 本地方法是一一對應的. ios
這樣就能夠根據方法名直接找到該函數.可是據我所知,只適合C語言,或者extern "C",可是一旦使用了這些,又會 shell
增長C調用C++的複雜度.說的有錯誤請指正. 函數
可是在使用jni開發中,JNIEXPORT 和 JNICALL 不是必須.咱們能夠徹底能夠手動進行本地方法的註冊. 工具
咱們會用到jni中的RegisterNatives函數對本地方法進行註冊.如下是例子重要代碼. spa
//jni.cpp code
#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,..) orm
而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工具編譯代碼,本身找教程吧.