1. 程式人生 > >Android:通過JNI呼叫c++程式碼

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