1. 程式人生 > >深入理解Android(4)——理解Android中的JNI(下)

深入理解Android(4)——理解Android中的JNI(下)

在前面文章中簡單介紹了JNI,這一篇文章來簡單看一下jni.h中定義的一些常用方法,來實現通過C++呼叫Android中的Java程式碼。

一、兩個引數的介紹

在前面的程式碼中我們會遇到兩個引數,下面對這兩個引數做一解釋

1、JNIEnv是指向可用JNI函式表的介面指標,C程式碼中JNIEnv是指向JNINativeInterface結構的指標,在C語言中JNIEnv必須作為第一個引數傳入每一個JNI函式的呼叫者,如:

(*env)->NewStringUTF(env, "helloworld");
在C++中,JNIEnv是C++類的例項,JNI函式以成員函式形式存在,所以在JNI環境中,無序傳入該引數,如:
env->NetStringUTF("helloworld");
2、jobject是一個指向java物件的引用,如果本地方法是一個靜態方法,則是指向類位元組碼檔案的class物件。

二、JNI資料型別

1、基本型別對映

從上表中可以看出,Java的資料型別和C++的基本資料型別有一個對映關係,我們在使用JNI的時候可以直接使用Natvie Type來操作Native層的資料,這樣就不用記憶複雜的對映關係了,從變數的名字上我們可以看到在Java的基本資料型別前面加一個字母‘j'就是對應的C++的Native型別。

2、引用型別對映


與基本型別不同的是,引用型別對原生的方法是不透明的(不能直接使用和修改),JNI提供了與這些引用型別密切相關的一組API,這些API通過JNIEnv介面指標提供給原生函式。

我們在jni.h中可以看到上面型別的定義:

class _jobject {};
class _jclass : public _jobject {};
class _jthrowable : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jbooleanArray : public _jarray {};
class _jbyteArray : public _jarray {};
class _jcharArray : public _jarray {};
class _jshortArray : public _jarray {};
class _jintArray : public _jarray {};
class _jlongArray : public _jarray {};
class _jfloatArray : public _jarray {};
class _jdoubleArray : public _jarray {};
class _jobjectArray : public _jarray {};

typedef _jobject *jobject;
typedef _jclass *jclass;
typedef _jthrowable *jthrowable;
typedef _jstring *jstring;
typedef _jarray *jarray;
typedef _jbooleanArray *jbooleanArray;
typedef _jbyteArray *jbyteArray;
typedef _jcharArray *jcharArray;
typedef _jshortArray *jshortArray;
typedef _jintArray *jintArray;
typedef _jlongArray *jlongArray;
typedef _jfloatArray *jfloatArray;
typedef _jdoubleArray *jdoubleArray;
typedef _jobjectArray *jobjectArray;
這些類的設計和Java一樣,都繼承自一個名為_jobject的父類。

三、對引用型別操作的例子

package com.example.test;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;

import com.example.myfirstjniproj.R;

/**
 * 陽光小強 http://blog.csdn.net/dawanganban
 * @author lixiaoqiang
 *
 */
public class MainActivity extends Activity implements OnClickListener{

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		findViewById(R.id.jni_jstring_button).setOnClickListener(this);
		findViewById(R.id.jni_javaArray_button).setOnClickListener(this);
	}

	@Override
	public void onClick(View view) {
		switch (view.getId()) {
		case R.id.jni_jstring_button:
			showToast(jniStringTest("input string"));
			break;
		case R.id.jni_javaArray_button:
			int[] array = jniArrayTest();
			showToast("arr[1]=" + array[1] + " : arr[2]=" + array[2]);
			break;
		default:
			break;
		}
	}
	
	private void showToast(String content){
		Toast.makeText(MainActivity.this, content, Toast.LENGTH_SHORT).show();
	}
	
	public native String jniStringTest(String str);
	
	public native int[] jniArrayTest();
	
	
	static{
		System.loadLibrary("jnitest");
	}
}
通過javah生成的標頭檔案
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_test_MainActivity */

#ifndef _Included_com_example_test_MainActivity
#define _Included_com_example_test_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_test_MainActivity
 * Method:    jniStringTest
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_test_MainActivity_jniStringTest
  (JNIEnv *, jobject, jstring);

/*
 * Class:     com_example_test_MainActivity
 * Method:    jniArrayTest
 * Signature: ()[I
 */
JNIEXPORT jintArray JNICALL Java_com_example_test_MainActivity_jniArrayTest
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
c++實現
#include "com_example_test_MainActivity.h"
#include <stdlib.h>
using namespace std;

JNIEXPORT jstring JNICALL Java_com_example_test_MainActivity_jniStringTest
  (JNIEnv * env, jobject obj, jstring str){
	//將java字串轉成c++字串
	jboolean isCopy;
	const char* cstr = env->GetStringUTFChars(str, &isCopy);
	//釋放原生字串
	env->ReleaseStringUTFChars(str, cstr);
	//建立字串
	jstring jstr = env->NewStringUTF("hello world");;

	return jstr;
}

JNIEXPORT jintArray JNICALL Java_com_example_test_MainActivity_jniArrayTest
  (JNIEnv * env, jobject obj){
	//建立一個10個元素的陣列
	jintArray intarray = env->NewIntArray(10);
	if(0 != intarray){
		jint nativeArray[10];
		//獲取原生的陣列
		env->GetIntArrayRegion(intarray, 0, 10, nativeArray);
		nativeArray[1] = 10;
		nativeArray[2] = 20;
		//設定改變
		env->SetIntArrayRegion(intarray, 0, 10, nativeArray);
	}
	return intarray;
}
四、訪問域和獲取ID

Java有兩個域:例項域和靜態域,每個例項都有自己的例項域副本,而一個類的所有例項共享一個靜態域。

JNIEXPORT jstring JNICALL Java_com_example_test_MainActivity_getInstanceField
  (JNIEnv * env, jobject obj){
	//通過物件獲得類
	jclass clazz;
	clazz = env->GetObjectClass(obj);
	//獲得例項域Id
	jfieldID instanceFieldId;
	instanceFieldId = env->GetFieldID(clazz, "instanceField", "Ljava/lang/String");
	//獲得例項域
	jobject instanceField;
	instanceField = env->GetObjectField(obj, instanceFieldId);
	jstring jstr = env->NewStringUTF("獲取成功");
	return jstr;
}


JNIEXPORT jstring JNICALL Java_com_example_test_MainActivity_getStaticField
  (JNIEnv * env, jobject obj){
	jclass clazz;
	clazz = env->GetObjectClass(obj);
	jfieldID staticFieldId;
	staticFieldId = env->GetStaticFieldID(clazz, "staticField", "Ljava/lang/String");
	jobject staticField;
	staticField = env->GetStaticObjectField(clazz, staticFieldId);
	jstring jstr = env->NewStringUTF("獲取成功");
	return jstr;
}
在上面程式碼中我們看到了“Ljava/lang/String"字串,這個是獲取ID的型別簽名,Java的型別簽名對映表如下:

五、呼叫Java中的方法

與上面的域一樣,Java中也有兩類方法,例項方法和靜態方法,JNI提供了兩類方法的函式

	public native void getInstanceMethod();
	
	public native void getStaticMethod();
	
	public String instanceMethod(){
		return "instanceMethod";
	}
	
	public static String staticMethod(){
		return "staticMethod";
	}
JNIEXPORT void JNICALL Java_com_example_test_MainActivity_getInstanceMethod
  (JNIEnv * env, jobject obj){
	jclass clazz;
	clazz = env->GetObjectClass(obj);
	jmethodID instanceMethodId;
	instanceMethodId = env->GetMethodID(clazz, "instanceMethod", "()Ljava/lang/String");
	jstring instanceMethodResult;
	instanceMethodResult = env->CallStringMethod(obj, instanceMethodId);
}

JNIEXPORT void JNICALL Java_com_example_test_MainActivity_getStaticMethod
  (JNIEnv * env, jobject obj){
	jclass clazz;
	clazz = env->GetObjectClass(obj);
	jmethodID staticMethodId;
	staticMethodId = env->GetStaticMethodID(clazz, "staticMethod", "()Ljava/lang/String");
	jstring staticMethodResult;
	staticMethodResult = env->CallStaticStringMethod(clazz, staticMethodId);
}