深入理解Android(4)——理解Android中的JNI(下)
阿新 • • 發佈:2019-01-01
在前面文章中簡單介紹了JNI,這一篇文章來簡單看一下jni.h中定義的一些常用方法,來實現通過C++呼叫Android中的Java程式碼。
一、兩個引數的介紹
在前面的程式碼中我們會遇到兩個引數,下面對這兩個引數做一解釋
1、JNIEnv是指向可用JNI函式表的介面指標,C程式碼中JNIEnv是指向JNINativeInterface結構的指標,在C語言中JNIEnv必須作為第一個引數傳入每一個JNI函式的呼叫者,如:
(*env)->NewStringUTF(env, "helloworld");
在C++中,JNIEnv是C++類的例項,JNI函式以成員函式形式存在,所以在JNI環境中,無序傳入該引數,如:2、jobject是一個指向java物件的引用,如果本地方法是一個靜態方法,則是指向類位元組碼檔案的class物件。env->NetStringUTF("helloworld");
二、JNI資料型別
1、基本型別對映
從上表中可以看出,Java的資料型別和C++的基本資料型別有一個對映關係,我們在使用JNI的時候可以直接使用Natvie Type來操作Native層的資料,這樣就不用記憶複雜的對映關係了,從變數的名字上我們可以看到在Java的基本資料型別前面加一個字母‘j'就是對應的C++的Native型別。
2、引用型別對映
與基本型別不同的是,引用型別對原生的方法是不透明的(不能直接使用和修改),JNI提供了與這些引用型別密切相關的一組API,這些API通過JNIEnv介面指標提供給原生函式。
我們在jni.h中可以看到上面型別的定義:
這些類的設計和Java一樣,都繼承自一個名為_jobject的父類。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;
三、對引用型別操作的例子
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;
}
四、訪問域和獲取IDJava有兩個域:例項域和靜態域,每個例項都有自己的例項域副本,而一個類的所有例項共享一個靜態域。
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);
}