1. 程式人生 > >ubuntu14.04下通過JNI使用C++跨語言呼叫java例項

ubuntu14.04下通過JNI使用C++跨語言呼叫java例項

軟體環境:

  • ubuntu14.04
  • eclipse
  • clion

一、java程式碼

在eclipse中新建Test工程,package為test,建立Demo類。Demo.java如下。

package test;

public class Demo {
			 //用於演示如何訪問靜態的基本型別屬性  
		    public static int COUNT = 8;  
		    //演示物件型屬性  
		    private String msg;  
		    private int[] counts;  
		      
		    public Demo()    {     this("預設建構函式");      }  
		 
		     //演示如何訪問構造器 
		    public Demo(String msg)     {  this.msg = msg;  this.counts = null;    }  	   
		    public String getMessage()     {       return msg;    }  
		   //該方法演示如何訪問一個靜態方法 
		    public static String getHelloWorld()   {  return "Hello world!";   }  
		  //該方法演示引數的傳入傳出及中文字元的處理 
		    public String append(String str, int i)   {  return str + i;        }   
		  // 演示陣列物件的訪問 
		    public int[] getCounts()  {    return counts;     } 
		  //演示如何構造一個數組物件 
		    public void setCounts(int[] counts)   {   this.counts = counts;   }  
		  // 演示異常的捕捉 
		    public void throwExcp()throws IllegalAccessException  
		    {  
		        throw new IllegalAccessException("exception occur.");  
		    }  
}

二、C++程式碼

在clion中新建一個demo1工程,main.cpp如下:

#include <jni.h>
#include <string>
#include <iostream>
#include <memory.h>
using namespace std;

char* jstringToChar(JNIEnv* env, jstring jstr) {
    char* rtn = NULL;
    jclass clsstring = env->FindClass("java/lang/String");
    jstring strencode = env->NewStringUTF("utf-8");//GB2312中文亂碼
    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;
    }
    env->ReleaseByteArrayElements(barr, ba, 0);
    return rtn;
}
jstring charTojstring(JNIEnv* env, const char* pat) {
    //定義java String類strClass
    jclass strClass = (env)->FindClass("Ljava/lang/String;");
    // 獲取String(byte[],String)的構造器,用於將本地byte[]陣列轉換為一個新String
    jmethodID ctorID = (env)->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
    //建立byte陣列
    jbyteArray bytes = (env)->NewByteArray(strlen(pat));
    // 將char* 轉換為byte陣列
    (env)->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*) pat);
    // 設定String, 儲存語言型別,用於byte陣列轉換至String時的引數
    jstring encoding = (env)->NewStringUTF("utf-8");
    // 將byte陣列轉換為java String,並輸出
    return (jstring) (env)->NewObject(strClass, ctorID, bytes, encoding);

}

int main() {
    char opt1[] = "-Djava.compiler=NONE"; /** 暫時不知道啥意思,網上抄來的 */
    char opt2[] = "-Djava.class.path=/home/hl/eclipse-workspace/Test/bin";  /** 指定Java類編譯後.class檔案所在的目錄 */
    char opt3[] = "-verbose:NONE";        /** 暫時不知道啥意思,網上抄來的 */

    JavaVMOption options[3];
    options[0].optionString = opt1; options[0].extraInfo = NULL;
    options[1].optionString = opt2; options[1].extraInfo = NULL;
    options[2].optionString = opt3; options[2].extraInfo = NULL;

    JavaVMInitArgs jargv;
    jargv.version = JNI_VERSION_1_6; /** JDK JNI VERSION*/
    jargv.nOptions = 3;
    jargv.options = options;
    jargv.ignoreUnrecognized = JNI_TRUE;

    JavaVM* jvm = NULL;
    JNIEnv* jenv = NULL;
    //建立虛擬機器
    jint res = JNI_CreateJavaVM( &jvm, (void**)&jenv, &jargv );
    if ( res < 0 )
        return -1;
    //查詢java類
    jclass cls = jenv->FindClass("test/Demo");
    if ( NULL == cls  ) {
        if( jenv->ExceptionOccurred()  )
            jenv->ExceptionDescribe();
        else {
            std::cout << "jc null" << std::endl;
        }
        return 1;
    }
    //呼叫靜態方法  public static String getHelloWorld()  { return "Hello world!"; }
    jmethodID mid = jenv->GetStaticMethodID(cls, "getHelloWorld","()Ljava/lang/String;");
    jstring msg = (jstring)jenv->CallStaticObjectMethod(cls, mid);
    cout<<jstringToChar(jenv, msg)<<endl;

    //呼叫靜態屬性  public static int COUNT = 8;
    jfieldID fid = jenv->GetStaticFieldID(cls, "COUNT","I");
    int count = (int)jenv->GetStaticIntField(cls, fid);
    cout<<count<<endl;

    //演示引數的傳入傳出及中文字元的處理 public String append(String str, int i) { return str + i; }
    jobject obj = jenv->AllocObject(cls);
    jmethodID mid1 = jenv->GetMethodID(cls, "append","(Ljava/lang/String;I)Ljava/lang/String;");
    //構造引數並呼叫物件的方法
    const char szTest1[] = "蘋果";
    jstring arg1 = charTojstring(jenv, szTest1);
    jstring msg1 = (jstring) jenv->CallObjectMethod(obj, mid1, arg1, 12);
    cout<<jstringToChar(jenv, msg1)<<endl;


    //呼叫建構函式public Demo(String msg) { this.msg = msg; this.counts = null;}
    jmethodID mid2 = jenv->GetMethodID(cls,"<init>","(Ljava/lang/String;)V");
    const char szTest2[] = "電信";
    jstring arg2 = charTojstring(jenv, szTest2);
    jobject demo = jenv->NewObject(cls,mid2,arg2);
    //驗證是否構造成功 public String getMessage() { return msg; }
    mid2 = jenv->GetMethodID(cls, "getMessage","()Ljava/lang/String;");
    jstring msg2 = (jstring)jenv->CallObjectMethod(demo, mid2);
    cout<<jstringToChar(jenv, msg2)<<endl;


    //傳入傳出陣列
    //構造陣列
    int arrayCpp[] = {1,3,5,7,9};
    jintArray array = jenv->NewIntArray(5);
    jenv->SetIntArrayRegion(array, 0, 5, arrayCpp);
    //傳入陣列  public void setCounts(int[] counts)  { this.counts = counts; }
    jobject obj1 = jenv->AllocObject(cls);
    jmethodID mid3 = jenv->GetMethodID(cls,"setCounts","([I)V");
    jenv->CallVoidMethod(obj1, mid3, array);
    //獲取陣列 public int[] getCounts()  {  return counts;  }
    mid3 = jenv->GetMethodID(cls,"getCounts","()[I");
    jintArray msg3 = (jintArray)jenv->CallObjectMethod(obj1, mid3, array);
    int len =jenv->GetArrayLength(msg3);
    jint* elems =jenv-> GetIntArrayElements(msg3, 0);
    for(int i=0; i< len; i++)
    {
        cout<<"ELEMENT "<<i<<" IS "<<elems[i]<<endl;
    }
    jenv->ReleaseIntArrayElements(msg3, elems, 0);


    //異常處理  public void throwExcp()throws IllegalAccessException {  throw new IllegalAccessException("exception occur."); }
    jobject obj2 = jenv->AllocObject(cls);
    jmethodID mid4 = jenv->GetMethodID(cls,"throwExcp","()V");
    jenv->CallVoidMethod(obj2, mid4);
    //獲取異常資訊
    string exceptionInfo = "";
    jthrowable excp = 0;
    excp = jenv->ExceptionOccurred();
    if(excp)
    {
        jclass cls = jenv->GetObjectClass(excp);
        jenv->ExceptionClear();
        jmethodID mid5 = jenv->GetMethodID(cls, "toString","()Ljava/lang/String;");
        jstring msg = (jstring) jenv->CallObjectMethod(excp, mid5);
        cout<<jstringToChar(jenv, msg)<<endl;
        jenv->ExceptionClear();
    }


    std::cout << "Hi , this is a c++ project!" << std::endl;
    return 0;
}

CMakelists.txt如下:

cmake_minimum_required(VERSION 3.8)
project(demo1)

set(CMAKE_CXX_STANDARD 11)
include_directories("/usr/local/java/jdk-9/include" "/usr/local/java/jdk-9/include/linux")
link_directories(/usr/local/java/jdk-9/lib/server )

set(SOURCE_FILES main.cpp)
add_executable(demo1 ${SOURCE_FILES})

target_link_libraries(
        demo1
        jvm
)

執行結果如下所示:

/home/hl/CLionProjects/demo1/cmake-build-debug/demo1
Hello world!
8
蘋果12
電信
ELEMENT 0 IS 1
ELEMENT 1 IS 3
ELEMENT 2 IS 5
ELEMENT 3 IS 7
ELEMENT 4 IS 9
java.lang.IllegalAccessException: exception occur.
Hi , this is a c++ project!

Process finished with exit code 0