概要概要
今日もJNIの勉強を続けています。最初のいくつかの記事でいくつかの基本的な内容を学びました。今日はそれらすべてを練習します。この記事は、将来忘れてしまった場合に備えて、メモにすぎません。
JavaオブジェクトのメンバーへのJNIアクセス
まず、使用する必要のあるAPIを見てみましょう。
jclassを取得する
jclass GetObjectClass(JNIEnv *env, jobject obj);
- jobject:Javaを表すオブジェクト
- この関数はクラスオブジェクトを返します
jfieldIDを取得する
jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
- JNIEnv:JNIEnvポインター
- jclass:受け渡し
GetObjectClass
またはFindClass
取得できるクラスオブジェクト - name:クラスオブジェクト内の変数の名前
- sig:変数の型シグネチャ
- クラスオブジェクトの変数を返します。タイプはjfieldIDです。
変数の値を取得する
変数の種類に応じて、変数の値を取得するためのさまざまな関数があります。関数の基本的な形式は次のとおりです。
NativeType Get<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID);
関数名 | 戻り値のタイプ |
---|---|
GetBooleanField() | jboolean |
GetByteField() | jbyte |
GetCharField() | jchar |
GetShortField() | jshort |
GetIntField() | jit |
GetLongField() | jlong |
GetFloatField() | jfloat |
GetDoubleField() | jdouble |
GetObjectField() | ジョブジェクト |
最初の8つの項目は基本データ型であり、9番目の項目はすべての参照データ型を取得することです。
たとえば、次のようにint変数を取得します。
jint GetIntField(JNIEnv * env, jobject obj, jfieldID fieldID);
- jobject:Javaオブジェクトを表します
- jfieldID:変数のクラスに代わって、
GetFieldID
取得することにより - ジントを返す
変数の値を設定します
変数の種類に応じて、変数の値を設定するためのさまざまな関数があります
void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID,
NativeType value);
関数名 | パラメータタイプ |
---|---|
SetBooleanField() | jboolean |
SetByteField() | jbyte |
SetCharField() | jchar |
SetShortField() | jshort |
SetIntField() | jit |
SetLongField() | jlong |
SetFloatField() | jfloat |
SetDoubleField() | jdouble |
SetObjectField() | ジョブジェクト |
最初の8つの項目は基本的なデータ型に対応し、9番目の項目はJavaのすべての参照型に対応します。
たとえば、intの関数プロトタイプ
void setIntField(JNIEnv * env, jobject obj, jfieldID fieldID, jint value);
- value:intの値を設定したいのですが、他のパラメータは上記と同じなので、繰り返しません。
jmethodIDを取得します
jmethodID GetMethodID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
- name:クラスオブジェクトのメソッドの名前を取得します
- sig:メソッドのシグネチャを取得します
- 戻り値:このメソッドが存在する場合はjmethodIDを返し、存在しない場合はNULLを返します。
- Java構築メソッドを取得した場合、const char * nameの値とパラメーターconstchar * sigの値はvoid(V)です。
オブジェクトメソッドを呼び出す
javaメソッドの戻り値に応じて、jniにはjavaオブジェクトのメソッドを呼び出すためのさまざまな関数があります。3つの基本的な形式があります。
NativeType Call<type>Method(JNIEnv *env, jobject obj,
jmethodID methodID, ...);
NativeType Call<type>MethodA(JNIEnv *env, jobject obj,
jmethodID methodID, const jvalue *args);
NativeType Call<type>MethodV(JNIEnv *env, jobject obj,
jmethodID methodID, va_list args);
これらの3つの方法の違いは、パラメーターの受け渡しとは異なります。最も一般的に使用されるのは最初の方法です。最初の方法についてのみ説明します。
メソッドがintを返す場合、関数プロトタイプは次のようになります。
jint CallIntMethod(JNIEnv * env, jobject obj, jmethodID methodID, ...);
メソッドの戻り値が参照データ型の場合
jobject CallObjectMethod(JNIEnv * env, jobject obj, jmethodID methodID, ...);
javaの戻り値タイプがvoidの場合
void CallVoidMethod(JNIEnv * env, jobject obj, jmethodID methodID, ...);
- jmethodID:メソッドオブジェクト、
GetMethodID
取得することにより - …:変数パラメーター、javaメソッドに渡す必要のあるパラメーター
以下に実装されている完全なコードを見てみましょう
最初に、オブジェクトの作成時に呼び出されるネイティブメソッドを持つJavaクラスを定義します
public class Student {
public String name = "小红";
static {
System.loadLibrary("studentlib");
}
native void native_init();
public Student() {
native_init();
}
public void print() {
Log.d("mmm", name);
}
}
このJavaネイティブメソッドをjniに登録してから、Cで実装します。
#include <jni.h>
#include <iostream>
#include <android/log.h>
static void native_init(JNIEnv *env, jobject jobject1) {
//1 获取java对象的变量的值,并重新为他设置新的值
//获取class对象
jclass jclass_student = env->GetObjectClass(jobject1);
//从class中获取变量
jfieldID jfieldId = env->GetFieldID(jclass_student, "name", "Ljava/lang/String;");
//从java对象obj中获取name变量的值
jstring name = static_cast<jstring>(env->GetObjectField(jobject1, jfieldId));
const char *char_name = env->GetStringUTFChars(name, JNI_FALSE);
//打印出原来的name变量的值
__android_log_print(ANDROID_LOG_INFO, "mmm", "method = %s, msg = %s", __FUNCTION__,
char_name);
const char native_name[] = "小明";
jstring jstring_name = env->NewStringUTF(native_name);
//通过jni重新设置变量name的值
env->SetObjectField(jobject1, jfieldId, jstring_name);
//2 调用java对象中的方法
//获取class对象中的print方法
jmethodID print = env->GetMethodID(jclass_student, "print", "()V");
//调用java对象中的print方法
env->CallVoidMethod(jobject1, print);
}
static const JNINativeMethod nativeMethod[] = {
{
"native_init", "()V", (void *) native_init},
};
static int registNativeMethod(JNIEnv *env) {
int result = -1;
jclass class_text = env->FindClass("com.taobao.alinnkit.ndk1.Student");
if (env->RegisterNatives(class_text, nativeMethod,
sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
result = 0;
}
return result;
}
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
int result = -1;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
if (registNativeMethod(env) == JNI_OK) {
result = JNI_VERSION_1_6;
}
return result;
}
}
動的登録についてnative_init
は前の記事で説明したので、ここではあまり説明しません。主な実装ロジックは実装にあります。一般的なロジックは、Student
オブジェクトname
内の変数の値を取得して出力し、値をリセットname
することです。変数の値を出力し、最後にStudent
Objectprint
メソッドを呼び出して、name
変数の値を出力します。
最後は呼び出すことです。これは、newによって直接トリガーできます。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Student student = new Student();
}
印刷結果を見てください
/mmm: method = native_init, msg = 小红
/mmm: 小明
Java静的メンバーへのJNIアクセス
ベルト付きのApiを見てみましょう
FindClass
クラスオブジェクトを取得する
jclass FindClass(JNIEnv *env, const char *name);
- const char * name:完全修飾クラス名
GetStaticFieldID
クラスオブジェクトの静的フィールドを取得します
jfieldID GetStaticFieldID (JNIEnv *env,
jclass clazz,
const char *name,
const char *sig);
- name:静的フィールドの名前
- sig:静的フィールドの型シグネチャ
GetStaticField
静的フィールドの値を取得します。静的フィールドのタイプに応じて、jniには静的フィールドの値を取得するためのさまざまなメソッドがあります。
メソッド名 | 戻り値 |
---|---|
GetStaticBooleanField | jboolean |
GetStaticByteField | jbyte |
GetStaticCharField | jchar |
GetStaticShortField | jshort |
GetStaticIntField | jit |
GetStaticLongField | jlong |
GetStaticFloatField | jfloat |
GetStaticDoubleField | jdouble |
GetStaticObjectField | ジョブジェクト |
静的変数の値を設定します
void SetStatic<type>Field(JNIEnv *env,
jclass clazz,
jfieldID fieldID,
NativeType value);
- 最後のパラメータは、設定する必要のある値です
メソッド名 | NativeType |
---|---|
SetStaticBooleanField | jboolean |
SetStaticByteField | jbyte |
SetStaticCharField | jchar |
SetStaticShortField | jshort |
SetStaticIntField | jit |
SetStaticLongField | jlong |
SetStaticFloatField | jfloat |
SetStaticDoubleField | jdouble |
SetStaticObjectField | ジョブジェクト |
静的メソッドを呼び出す
jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...)
これら2つは以前に使用された通常の方法と同じですが、関数名が変更されています。
完全なコード
Javaコード
public class Student {
public static String text = "没改过";
static {
System.loadLibrary("studentlib");
}
native void native_Text();
public static void printText() {
Log.d("mmm", text);
}
}
cコード
static void native_text(JNIEnv *env, jobject jobject1) {
//获取class
jclass jclass_student = env->FindClass("com.taobao.alinnkit.ndk1.Student");
//获取静态变量text
jfieldID jfieldId_text = env->GetStaticFieldID(jclass_student, "text", "Ljava/lang/String;");
//获取静态变量text的值
jstring text = (jstring) env->GetStaticObjectField(jclass_student, jfieldId_text);
const char *char_name = env->GetStringUTFChars(text, JNI_FALSE);
__android_log_print(ANDROID_LOG_INFO, "mmm", "method = %s, msg = %s", __FUNCTION__,
char_name);
jstring jstring1 = env->NewStringUTF("改过了");
//修改静态变量text的值
env->SetStaticObjectField(jclass_student, jfieldId_text, jstring1);
//获取静态方法printText
jmethodID jmethodId = env->GetStaticMethodID(jclass_student, "printText", "()V");
//调用静态方法printText
env->CallStaticVoidMethod(jclass_student, jmethodId);
}
static const JNINativeMethod nativeMethod[] = {
{
"native_Text", "()V", (void *) native_text}
};
static int registNativeMethod(JNIEnv *env) {
int result = -1;
jclass class_text = env->FindClass("com.taobao.alinnkit.ndk1.Student");
if (env->RegisterNatives(class_text, nativeMethod,
sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
result = 0;
}
return result;
}
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
int result = -1;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
if (registNativeMethod(env) == JNI_OK) {
result = JNI_VERSION_1_6;
}
return result;
}
}
主な実装はnative_text
、主にtext
静的変数の値を取得し、静的変数の値を変更しtext
、静的メソッドを呼び出すメソッドです。printText
転送
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Student student = new Student();
student.native_Text();
}
データを印刷する
/mmm: method = native_text, msg = 没改过
/mmm: 改过了
参照
https://juejin.im/post/5d23fb066fb9a07eb15d7b29
https://blog.csdn.net/afei__/article/details/81016413
https://www.jianshu.com/p/67081d9b0a9c