Android:通過JNI呼叫c++程式碼
一些說明
//查詢JNIAPI類,得到類的class,包名每級用/分隔
jclass clazz = env->FindClass("com/g/im/JNIAPI");
//通過類查詢靜態方法,得到方法的引用,callBackLogin為方法名稱,"Ljava/lang/String;"表示函式引數為String型別,要用括號括起來,V表示函式返回值為void
jmethodID methodId = env -> GetStaticMethodID(clazz, "callBackLogin", "(Ljava/lang/String;)V")
//呼叫此方法,第三個引數為
env->CallStaticVoidMethod(clazz, id, jstring型別的字串)
在JNI中與Java型別對應的型別如下,一個方法有多個引數時直接組合使用:
類型別:L包名/類名; 需要以分號結尾,如:Ljava/lang/String;
類型別[]:[L包名/類名; 需要以分號結尾,如:[Ljava/lang/String;
boolean:Z 如:env -> GetStaticMethodID(clazz, "callBackLogin", "(Z)V")
boolean[]:[Z
byte:B
byte[]:[B
char:C
char[]:[C
short:S
short[]:[S
int:I
int[]:[I
long:J
long[]:[J
float:F
float[]:[F
double:D
double[]:[D
void:V
Java呼叫類:
/** * Java與C/C++互調 * (JNI呼叫動態庫介面) */ public class JNIAPI { static { System.loadLibrary("GIM"); } /** * 登入 */ public native void login(String username, String ticket, String deviceId); /** * C++層非同步回撥 */ public void callbackLogin(String jsonData) { //解析返回的資料 } }
與Java對應的.h標頭檔案:
#include <jni.h>
#ifndef _Included_com_g_im_biz_JNIAPI
#define _Included_com_g_im_biz_JNIAPI
#ifdef __cplusplus
extern "C" {
#endif
/**
* 登入
*/
JNIEXPORT void JNICALL Java_com_g_im_biz_JNIAPI_login(JNIEnv *, jobject, jstring, jstring, jstring);
#ifdef __cplusplus
}
#endif
#endif
.h的實現cpp檔案,此類用於java與c++互動資料用:
#include <string.h>
#include <jni.h>
#include <locale.h>
#include <stdlib.h>
#include <list>
#include <vector>
#include "com_g_im_biz_JNIAPI.h"
#include "com_g_im_biz_JNIAPI_impl.h"
#include "GTPIM/common/CommUtility.h"
#include<stdio.h>
#include <android/log.h>
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "ProjectName", __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , "ProjectName", __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO , "ProjectName", __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN , "ProjectName", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , "ProjectName", __VA_ARGS__)
JavaVM* mJvm;
jobject mJNIAPI_obj;
jobject mStr_obj;
void processSignal(int signo);
/**jstring轉換char**/
char* jstringToChar(JNIEnv* env, jstring jstr) {
if (jstr == NULL){
return NULL;
}
char* rtn = new char;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("utf-8");
jmethodID mid = env->GetMethodID(clsstring, "getBytes","(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0){
rtn = (char*) malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}else {
rtn = "";
}
/**資源清理**/
env->ReleaseByteArrayElements(barr, ba, 0);
if(clsstring != NULL){
env->DeleteLocalRef(clsstring);
clsstring = NULL;
}
if(strencode != NULL){
env->DeleteLocalRef(strencode);
strencode = NULL;
}
mid = NULL;
return rtn;
}
/**char轉換jstring**/
jstring charToJstring(JNIEnv* env, const char* pat){
jclass str_class = env->GetObjectClass(mStr_obj);
jmethodID ctorID = env->GetMethodID(str_class, "<init>", "([BLjava/lang/String;)V");
jbyteArray bytes = env->NewByteArray(strlen(pat));
env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);
jstring encoding = env->NewStringUTF("utf-8");
jstring str = (jstring)env->NewObject(str_class, ctorID, bytes, encoding);
jbyte* ba = env->GetByteArrayElements(bytes, JNI_FALSE);
/**資源清理**/
env->ReleaseByteArrayElements(bytes, ba, 0);
if(str_class != NULL){
env->DeleteLocalRef(str_class);
str_class = NULL;
}
if(encoding != NULL){
env->DeleteLocalRef(encoding);
encoding = NULL;
}
ctorID = NULL;
return str;
}
/**得到物件**/
jobject getInstance(JNIEnv* env, jclass JNIAPI_class){
jmethodID JNIAPI_method = env->GetMethodID(JNIAPI_class, "<init>", "()V");
jobject JNIAPI_obj = env->NewObject(JNIAPI_class, JNIAPI_method);
return JNIAPI_obj;
}
/**
* 回撥java中的方法
* @param methodName 方法名
* @param jsonData json資料
*/
void callbackJava(const char* methodName, const char* jsonData){
JNIEnv* env;
int status = mJvm->AttachCurrentThread(&env, NULL);
if(status >= 0){
jclass JNIAPI_class = env->GetObjectClass(mJNIAPI_obj);
jmethodID JNIAPI_method = env->GetMethodID(JNIAPI_class, methodName,"(Ljava/lang/String;)V");
if(JNIAPI_method != 0){
jstring str = charToJstring(env, xmlData);
env->CallVoidMethod(mJNIAPI_obj, JNIAPI_method, str);
/**資源清理**/
if(JNIAPI_class != NULL){
env->DeleteLocalRef(JNIAPI_class);
JNIAPI_class = NULL;
}
if(str != NULL){
env->DeleteLocalRef(str);
str = NULL;
}
JNIAPI_method = NULL;
}
if(strcmp(methodName, "printSignLog") != 0 && strcmp(methodName,"addMsgCache") != 0 && strcmp(methodName,"removeMsgCache") != 0){
mJvm->DetachCurrentThread();
}
}
}
/**
* 登入
*/
JNIEXPORT void JNICALL Java_com_glodon_im_service_JNIAPI_login(JNIEnv* env, jobject thiz, jstring name, jstring ticket, jstring deviceId) {
char* rtticket = jstringToChar(env, ticket);
char* rtdeviceId = jstringToChar(env, deviceId);
comm.login(rtname, rtticket, rtdeviceId);
if(rtname != NULL){
free(rtname);
rtname = NULL;
}
if(rtticket != NULL){
free(rtticket);
rtticket = NULL;
}
if(rtdeviceId != NULL){
free(rtdeviceId);
rtdeviceId = NULL;
}
}
void callbackLogin(const char* jsonData){
callbackJava("callbackLogin", jsonData);
}
與上面對應的.h標頭檔案:
#include <string.h>
#include <jni.h>
#include <locale.h>
#include <stdlib.h>
#include <signal.h>
#include <list>
#include <vector>
#include <android/log.h>
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "ProjectName", __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , "ProjectName", __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO , "ProjectName", __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN , "ProjectName", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , "ProjectName", __VA_ARGS__)
void callbackLogin(const char* jsonData);
Android.mk檔案:
#指定Android.mk的路徑
LOCAL_PATH := $(call my-dir)
#用於重置除LOCAL_PATH變數外的,所有LOCAL_XXX系列變數
include $(CLEAR_VARS)
#使用標準庫,系統預設使用system(最小的C++執行庫,部分功能將無法支援)
APP_STL := stlport_static
include $(BUILD_MULTI_PREBUILT)
#在所有原始檔中增加一個巨集定義#define MARKUP_STDCONV
LOCAL_CFLAGS := \
-DMARKUP_STDCONV
#生成的so檔名
LOCAL_MODULE := GIM
$(warning $(SYSROOT))
#新增系統庫
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
#需要參與編譯的cpp原始檔
LOCAL_SRC_FILES := \
com_g_im_biz_JNIAPI_impl.cpp \
#編譯靜態庫
#include $(BUILD_STATIC_LIBRARY)
#編譯動態庫
include $(BUILD_SHARED_LIBRARY)
Application.mk檔案:
# C++異常 C++ RTTI C++標準庫
#System預設的 不支援 不支援 不支援
#gabi++_shared、gabi++_static 不支援 支援 不支援
#stlport_shared、stlport_static 不支援 支援 支援
#gnustl_shared、gnustl_static 支援 支援 支援
APP_STL := gnustl_static
#表示生成哪些CPU架構的so,有這些值:all、armeabi、armeabi-v7a、x86、mips
#APP_ABI := armeabi armeabi-v7a
#STLPORT_FORCE_REBUILD := true
#可以使用APP_GNUSTL_CPP_FEATURES,指使用C++異常(exceptions)或C++ RTTI特性(rtti)
APP_CPPFLAGS += -fexceptions
#NDK版本號
APP_PLATFORM := android-9