Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> jni中java與原生代碼通信規則

jni中java與原生代碼通信規則

編輯:關於Android編程

1、前言

通過jni可以讓java和原生語言進行通信,這個通信不僅僅是信息傳遞,還包括方法間的調用,參數的傳遞。但是由於java的數據類型和原生語言的數據類型還是有所差異的,並且它們的實現機制不同,所以就需要將java中的對象和原生語言對象一一對應起來。在這個對應過程中,其實是很繁瑣並且開銷很大的,所以一般會SWIG自動生成代碼。但是為了學習這個過程,我們必須知道整個轉換的過程是怎樣的。

本文不講解jni項目的生成和調用過程,讀者如果需要可以自行學習也可以參考:了解jni的調用流程。

2、數據類型的類別

在java中,數據類型可以分為兩大類,一類是基本數據類型,一類是引用類型。

基本數據類型:bool,char,byte,short,int,long,float,double。

引用類型:除了基本類型以外的所有數據類型,包括數組。

通過jni的類型定義,可以讓java的基本數據類型和原生語言的基本類型相映射。意思就是說,通過jni.h頭文件,我們就可以通過jni頭文件中定義的類型在原生語言實現中來操作java對象。jni中類型定義的java基本數據類型與原生語言的基本類型對應關系如下表:

\

我們要知道的就是JNI類型對應的JAVA類型即可。

對於java的引用類型,它的內部數據結構並不向原生代碼公開,因此也就無法一一映射起來,但是在JNI類型中有一個jobject類型,基本是涵蓋了所有的引用類型的。而接下來看到的引用類型隱射表,所對應的原聲類型這一列對象全部都是jobject的子類。

\

對於以上的映射關系,我們必須清楚的知道它們所對應的java數據類型。

由於java的基本數據類型與原生語言的基本類型可以一一對應,那麼在使用的時候就不需要進行轉換了,直接使用對應的jni類型或原生語言類型。但是對於引用類型來說,它的轉換比較復雜,所以必須了解他的轉換過程。

3、對引用類型進行操作

由於引用類型對原生語言不是公開的,因此在使用的時候就必須進行轉換了。所以接下來就會講解各種轉換情況。


3.1 String類型的處理

java的字符串類型被原生語言當作引用類型來處理,因此如果想在原生語言中使用字符串類型的數據,就必須進行轉換。JNI機制提供兩種編碼方式的的字符串格式,分別是Unicode和UTF編碼格式。針對不同的編碼格式有不同的轉換方法。

3.1.1 將C風格字符串轉換為java字符串

可以將c風格字符串轉換為jni類型的jstring然後返回給java對象使用。如果是Unicode編碼則使用NewString,UTF編碼則用NewStringUTF函數。比如:

 

env->NewStringUTF("來自C++");

3.1.2 在原生語言中使用java字符串

 

java字符串在原生語言中被當作引用類型來處理,因此使用之前必須轉換為char數組類型。如果是Unicode的編碼使用GetStringChars,如果是UTF編碼則使用GetStringUTFChars。比如:

 

	const char *p;
	jboolean isCopy = JNI_TRUE;
	p = env->GetStringUTFChars(param, &isCopy);

在上面由於GetStringUTFChars方法返回一個指向char數組首地址的指針,因此需要定義一個char指針變量。它的第二個參數表示返回的指針指向的是堆中的對象還是字符串副本,true表示指向副本。JNI類型定義了兩個常量代表jboolean的真假,分別是

 

 

#define JNI_FALSE   0
#define JNI_TRUE    1

3.1.3 釋放字符串

 

穿件了字符串指針,使用完畢後應該釋放從而避免內存洩漏。釋放方法如果是Unicode編碼使用releaseStringChars,如果是UTF編碼使用releaseStringUTFChars。比如

 

env->ReleaseStringUTFChars(javaString, p);

javaString指的是java字符串對象,p指的是簽名創建的字符指針對象。

 

3.2 數組的處理

由於數組也被當作引用對象,所以JNI也提供函數對數組進行處理。

3.2.1 創建java數組

通過NewArray函數就可以在原生語言中創建java數組,type指的是Bool,Int,Float等基本數據類型。比如:

 

jintArray array1 = env->NewIntArray(10);

10表示數組的容量。

 

3.2.2 訪問java數組元素

通過GetArrayRegion函數就可以將java數組元素復制到一個C數組中,從而可以在原生語言中對其進行操作。比如:

 

jint nativeArray[10];
	//將java數組轉換成c數組
env->GetIntArrayRegion(array, 0, 10, nativeArray);

這樣,我們就可以操作nativeArray來訪問java數組的元素了。注意這裡是復制的方式,雖然訪問的內容是一樣的,但是他們所代表的對象不是同一個對象,而是java數組的一個副本。

 

3.2.3 將c數組復制到java數組

由於通過GetArrayRegion函數獲取的只是java數組的副本,因此任何對c數組的操作都不會影響java數組,而如果希望將c數組操作後的結果復制會Java數組,就需要使用setArrayRegion函數。比如:

 

env->SetIntArrayRegion(array, 0, 10, nativeArray);


3.2.4 通過指針操作java數組

 

由於復制的代價很高,尤其是在數組元素很多的情況下,因此使用指針的方式會更加合理。通過GetArrayElements函數就可以獲取一個指向java數組的指針。比如:

 

jint *pNative;
jboolean isCopy=JNI_TRUE;
pNative=env->GetIntArrayElements(array,&isCopy);

3.2.5 釋放指針對象

 

操作完成後,需要通過ReleaseArrayElements函數釋放指針。比如:

 

env->ReleaseIntArrayElements(array,pnative,0);
0表示釋放模式。總共有三種釋放模式:

 

\

復制回來的意思是,將原生數組的內容復制到java數組。

3.3 原生語言訪問java變量

不僅java可以調用原生語言,原生語言同樣可以調用java對象。在java中,類有兩個域,分別是靜態域和實例域。一個類可以有多個實例域,但是這多個實例域都對應者一個靜態域。而在原生語言中獲取它們的方法也是不同的。

3.3.1 根據調用對象獲取類

要想獲取類的對象的實例域或者靜態域,就必須知道類。獲取當前調用對象的類的方法如下:

 

jclass clazz;
clazz = env->GetObjectClass(jTiss);

有了類,我們就可以獲取域ID了。

 

3.3.2 獲取域ID

要想獲取java對象的實例域或者靜態域,除了知道所屬的類以外,還必須知道它的域ID。根據實例域和靜態域,獲取的方法也有所不同。

實例域,比如:

 

jfieldID fieldID;
fieldID = env->GetFieldID(clazz, "instanceString", "Ljava/lang/String;");
其中第二個參數表示java類中的實例變量的名稱,第三個參數是方法描述符,是Jni特有的一種。

 

靜態域,比如:

 

jfieldID fieldID;
fieldID = env->GetStaticFieldID(clazz, "staticString",
			"Ljava/lang/String;");

參數同上。但是此方法獲取的java對象的靜態變量。

 

 

3.3.3 獲取java變量的值

 

有了上述的域ID,就可以獲取java的實例變量或者靜態變量了。

獲取實例變量:

 

jstring string;
string = (jstring) env->GetObjectField(jTiss, fieldID);
獲取靜態變量:

 

 

jstring string;
string = (jstring) env->GetStaticObjectField(clazz, fieldID);



3.4 原生語言調用java方法

 

同樣的首先我們必須知道所調用的對象的類,接著需要知道方法ID:

獲取實例方法ID:

 

jclass clazz = env->GetObjectClass(jThis);
jmethodID methodID = env->GetMethodID(clazz, "getInstanceStringFromJava",
			"()Ljava/lang/String;");
其中第二個參數是方法名稱。

 

獲取靜態方法ID:

 

jclass clazz = env->GetObjectClass(jThis);
jmethodID methodID = env->GetStaticMethodID(clazz,"getStaticStringFromJava", "()Ljava/lang/String;");

參數同上。

 

有了方法ID就可以調用了。

調用實例方法:

 

env->CallObjectMethod(jThis, methodID);

調用靜態方法:

 

 

env->CallStaticObjectMethod(clazz, methodID);


4、以實際例子總結以上知識點

 

上面基本涵蓋了最基礎的知識點,下面通過例子練手。

給出頭文件代碼:

 

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
#include 
#include 
#include 
/* Header for class com_example_jnitestthree_CppUtils */

#ifndef _Included_com_example_jnitestthree_CppUtils
#define _Included_com_example_jnitestthree_CppUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jnitestthree_CppUtils
 * Method:    getStringFromCPP
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_jnitestthree_CppUtils_getStringFromCPP
  (JNIEnv *, jobject);

/*
 * Class:     com_example_jnitestthree_CppUtils
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_example_jnitestthree_CppUtils_add
  (JNIEnv *, jobject, jint, jint);

/*
 * Class:     com_example_jnitestthree_CppUtils
 * Method:    putJavaStringToJni
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_jnitestthree_CppUtils_putJavaStringToJni
  (JNIEnv *, jobject, jstring);

/*
 * Class:     com_example_jnitestthree_CppUtils
 * Method:    testArray
 * Signature: ([I)[I
 */
JNIEXPORT jintArray JNICALL Java_com_example_jnitestthree_CppUtils_testArray
  (JNIEnv *, jobject, jintArray);

/*
 * Class:     com_example_jnitestthree_CppUtils
 * Method:    getInstanceString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_jnitestthree_CppUtils_getInstanceString
  (JNIEnv *, jobject);

/*
 * Class:     com_example_jnitestthree_CppUtils
 * Method:    getStaticString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_jnitestthree_CppUtils_getStaticString
  (JNIEnv *, jobject);

/*
 * Class:     com_example_jnitestthree_CppUtils
 * Method:    callInstanceMethodByNative
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_jnitestthree_CppUtils_callInstanceMethodByNative
  (JNIEnv *, jobject);

/*
 * Class:     com_example_jnitestthree_CppUtils
 * Method:    callStaticMethodByNative
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_jnitestthree_CppUtils_callStaticMethodByNative
  (JNIEnv *, jobject);


#ifdef __cplusplus
}
#endif
#endif
源文件:

 

 

#include 

/* Class:     com_example_jnitestthree_CppUtils
 * Method:    getStringFromCPP
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_jnitestthree_CppUtils_getStringFromCPP(
		JNIEnv *env, jobject jThiss) {
	return env->NewStringUTF("來自C++");
}

/*
 * Class:     com_example_jnitestthree_CppUtils
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_example_jnitestthree_CppUtils_add(JNIEnv *env,
		jobject jThiss, jint num1, jint num2) {
	//基本類型的java和C++通過jni類型映射起來了,因此可以直接使用
	return num1 + num2;
}

/*
 * Class:     com_example_jnitestthree_CppUtils
 * Method:    putJavaStringToJni
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT jstring JNICALL Java_com_example_jnitestthree_CppUtils_putJavaStringToJni(
		JNIEnv *env, jobject jThiss, jstring param) {

	const char *p;
	jboolean isCopy = JNI_TRUE;
	p = env->GetStringUTFChars(param, &isCopy);
	jstring javaString = env->NewStringUTF(p);
	env->ReleaseStringUTFChars(javaString, p);
	return javaString;
}

/*
 * Class:     com_example_jnitestthree_CppUtils
 * Method:    testArray
 * Signature: ([I)Ljava/lang/String;
 */
JNIEXPORT jintArray JNICALL Java_com_example_jnitestthree_CppUtils_testArray(
		JNIEnv *env, jobject jThiss, jintArray array) {
	jintArray array1 = env->NewIntArray(10);
	jintArray sumArray = env->NewIntArray(10);
	jint nativeArray[10], nativeArray1[10], nativeSumArray[10];
	//將java數組轉換成c數組
	env->GetIntArrayRegion(array, 0, 10, nativeArray);
	env->GetIntArrayRegion(array1, 0, 10, nativeArray1);
	env->GetIntArrayRegion(sumArray, 0, 10, nativeSumArray);
	for (jint i = 0; i < 10; i++) {
		nativeArray1[i] = i + 1;
		nativeSumArray[i] = nativeArray[i] + nativeArray1[i];
	}
	env->SetIntArrayRegion(sumArray, 0, 10, nativeSumArray);
	return sumArray;
}

/*
 * Class:     com_example_jnitestthree_CppUtils
 * Method:    getInstanceString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_jnitestthree_CppUtils_getInstanceString(
		JNIEnv *env, jobject jTiss) {
	//首先獲取類對象
	jclass clazz;
	clazz = env->GetObjectClass(jTiss);
	//獲取要操作的對象的ID
	jfieldID fieldID;
	fieldID = env->GetFieldID(clazz, "instanceString", "Ljava/lang/String;");
	jstring string;
	string = (jstring) env->GetObjectField(jTiss, fieldID);
	return string;
}

/*
 * Class:     com_example_jnitestthree_CppUtils
 * Method:    getStaticString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_jnitestthree_CppUtils_getStaticString(
		JNIEnv *env, jobject jTiss) {

	jclass clazz;
	clazz = env->GetObjectClass(jTiss);
	jfieldID fieldID;
	fieldID = env->GetStaticFieldID(clazz, "staticString",
			"Ljava/lang/String;");
	jstring string;
	string = (jstring) env->GetStaticObjectField(clazz, fieldID);
	return string;
}

/*
 * Class:     com_example_jnitestthree_CppUtils
 * Method:    callInstanceMethodByNative
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_jnitestthree_CppUtils_callInstanceMethodByNative(
		JNIEnv *env, jobject jThis) {
	jclass clazz = env->GetObjectClass(jThis);
	jmethodID methodID = env->GetMethodID(clazz, "getInstanceStringFromJava",
			"()Ljava/lang/String;");
	jstring string = (jstring) env->CallObjectMethod(jThis, methodID);
	return string;
}

/*
 * Class:     com_example_jnitestthree_CppUtils
 * Method:    callStaticMethodByNative
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_jnitestthree_CppUtils_callStaticMethodByNative(
		JNIEnv *env, jobject jThis) {
	jclass clazz = env->GetObjectClass(jThis);
	jmethodID methodID = env->GetStaticMethodID(clazz,
			"getStaticStringFromJava", "()Ljava/lang/String;");
	jstring string = (jstring) env->CallStaticObjectMethod(clazz, methodID);
	return string;
}


java原生函數聲明:

 

 

package com.example.jnitestthree;

import java.io.ByteArrayOutputStream;

public class CppUtils {

	static {
		System.loadLibrary("cppUtils");
	}

	/**
	 * 從CPP獲取字符串
	 * 
	 * @return
	 */
	public native String getStringFromCPP();

	/**
	 * 進行加操作
	 * 
	 * @param num1
	 * @param num2
	 * @return
	 */
	public native int add(int num1, int num2);

	/**
	 * 將數組傳遞給jni
	 * 
	 * @param param
	 */
	public native String putJavaStringToJni(String param);

	/**
	 * 測試數組
	 * 
	 * @param array
	 * @return
	 */
	public native int[] testArray(int[] array);

	public String getArrayByString(int[] array) {
		StringBuilder sb = new StringBuilder();
		int[] sum = testArray(array);
		for (int i = 0; i < sum.length; i++) {
			sb.append(sum[i] + ",");
		}
		return sb.toString();
	}

	public String instanceString = "instanceString";
	public static String staticString = "staticString";

	/**
	 * 原生語言調用的對象實例和靜態實例
	 * 
	 * @return
	 */
	public native String getInstanceString();

	public native String getStaticString();

	/**
	 * 原生語言調用的實例方法和靜態方法
	 */
	public String getInstanceStringFromJava() {
		return "instanceStringFromJava";
	}

	public native String callInstanceMethodByNative();

	public static String getStaticStringFromJava() {
		return "staticStringFromJava";
	}

	public native String callStaticMethodByNative();
}
	

運行結果:

\

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved