查看原文
其他

NDK开发中Native与Java交互

哆啦安全 2022-05-24

The following article is from 字节流动 Author By


阅读本文大概需要 2.35 分钟。


前面文章分别介绍的 NDK 编译方式和第三方库的引入方式:

1. Java 与 JNI 数据类型对应关系

Java 数据类型JNI 数据类型

boolean

jboolean

byte

jbyte

char

jchar

short

jshort

int

jint

long

jlong

float

jfloat

double

jdouble

String

jstring

Object

jobject

byte[]

jbyteArray

Object[]

jobjectArray

2. Native 中的签名

NDK 开发中会用到 Java 对象的属性签名和方法签名,用于区分不同的属性和方法。

属性签名

属性的签名就是属性类型的简称。
属性类型与其签名的对应关系如下:

属性类型类型签名
booleanZ
byteB
charC
shortS
intI
longL
floatF
doubleD
voidV
StringLjava/lang/String;
Object以 L 开头,后加完整的包名,并以分号结束
byte[][B
Object[]以 [ 开头,后加对象类型签名,例如 String[] 对应 [Ljava/lang/String;

2. 方法签名

方法签名格式:
(参数类型签名)返回值类型签名

那么如何获取方法签名?
执行命令 javah <class 文件的全名>
如 
javah com.haohao.hellojni.NativeTest.class

生成 com_haohao_hellojni_NativeTest.h 文件,文件中便标出了方法的 Signature 。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_haohao_hellojni_NativeTest */

#ifndef _Included_com_haohao_hellojni_NativeTest
#define _Included_com_haohao_hellojni_NativeTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     com_haohao_hellojni_NativeTest
* Method:    getString
* Signature: ()Ljava/lang/String;
*/

JNIEXPORT jstring JNICALL Java_com_haohao_hellojni_NativeTest_getString__
 (JNIEnv *, jobject)
;

/*
* Class:     com_haohao_hellojni_NativeTest
* Method:    getString
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/

JNIEXPORT jstring JNICALL Java_com_haohao_hellojni_NativeTest_getString__Ljava_lang_String_2
 (JNIEnv *, jobject, jstring)
;

/*
* Class:     com_haohao_hellojni_NativeTest
* Method:    getInt
* Signature: ()I
*/

JNIEXPORT jint JNICALL Java_com_haohao_hellojni_NativeTest_getInt__
 (JNIEnv *, jobject)
;

/*
* Class:     com_haohao_hellojni_NativeTest
* Method:    getInt
* Signature: (I)I
*/

JNIEXPORT jint JNICALL Java_com_haohao_hellojni_NativeTest_getInt__I
 (JNIEnv *, jobject, jint)
;

/*
* Class:     com_haohao_hellojni_NativeTest
* Method:    input
* Signature: (Ljava/lang/String;I)V
*/

JNIEXPORT void JNICALL Java_com_haohao_hellojni_NativeTest_input__Ljava_lang_String_2I
 (JNIEnv *, jobject, jstring, jint)
;

/*
* Class:     com_haohao_hellojni_NativeTest
* Method:    input
* Signature: ([Ljava/lang/String;I)V
*/

JNIEXPORT void JNICALL Java_com_haohao_hellojni_NativeTest_input___3Ljava_lang_String_2I
 (JNIEnv *, jobject, jobjectArray, jint)
;

/*
* Class:     com_haohao_hellojni_NativeTest
* Method:    getBytes
* Signature: ([Ljava/lang/String;Ljava/lang/String;)[B
*/

JNIEXPORT jbyteArray JNICALL Java_com_haohao_hellojni_NativeTest_getBytes
 (JNIEnv *, jobject, jobjectArray, jstring)
;

#ifdef __cplusplus
}
#endif
#endif

3. Native 与 Java 交互

设置一个简单的类 NativeUtils.java 用于测试,Native 与 Java 交互的实现类似于 Java 的反射机制。

package com.haohao.hellojni;

import android.util.Log;

/**
* author: haohao
* time: 2018/1/1
* mail: haohaochang86@gmail.com
* desc: description
*/

public class NativeUtils {

   static {
       System.loadLibrary("native-utils");
   }

   private static final String TAG = "NativeUtils";
   public static int staticProp = -1;
   public int prop = -1;


   public String getStringFromJava(String str){
       Log.i(TAG, "getStringFromJava: "+ str);
       return "Hello C , I am from Java.";
   }

   public static String getStringFromJavaStatic(String str){
       Log.i(TAG, "getStringFromJavaStatic: " + str);
       return "Hello C , I am from Java static.";
   }

   public native void accessJavaClassProp();

   public native void callJavaClassMethod();

   public static native void accessStaticJavaProp();

   public static native void callStaticJavaMethod();

}

访问 Java 对象的非静态属性

JNIEXPORT void JNICALL
Java_com_haohao_hellojni_NativeUtils_accessJavaClassProp(JNIEnv *env, jobject instance)
{
   //访问 Java 对象的非静态属性

   //通过 JNIEnv 和对象 instance 实例拿到 class 。
   jclass cls = (*env)->GetObjectClass(env, instance);
   //获取属性的 field id
   jfieldID fid = (*env)->GetFieldID(env, cls, "prop", "I");
   //通过 field id 获取属性的值
   jint prop = (*env)->GetIntField(env, instance, fid);
   //在 Native 层修改属性
   prop += 101;
   (*env)->SetIntField(env, instance, fid, prop);

}

调用 Java 对象的非静态方法

JNIEXPORT void JNICALL
Java_com_haohao_hellojni_NativeUtils_callJavaClassMethod(JNIEnv *env, jobject instance)
{
   //调用 Java 对象的非静态方法

   jclass myClass = (*env)->GetObjectClass(env, instance);
   //获取发方法的 method id
   jmethodID mid = (*env)->GetMethodID(env, myClass, "getStringFromJava", "(Ljava/lang/String;)Ljava/lang/String;");
   //调用 Java 对象的方法
   jstring  jstr = (*env)->CallObjectMethod(env, instance, mid, (*env)->NewStringUTF(env, "Hello Java, I am From C."));
   //jstring 转换为 c 的字符串
   const char* cstr = (*env)->GetStringUTFChars(env, jstr, NULL);
   LOGD("%s", cstr);
   //注意区别对待 Java 字符串和 C 的字符串,除了基本数据类型之外,其他都需要进行类型转换
   //释放资源
   (*env)->ReleaseStringUTFChars(env, jstr, cstr);

}

访问 Java 对象的静态属性

JNIEXPORT void JNICALL
Java_com_haohao_hellojni_NativeUtils_accessStaticJavaProp(JNIEnv *env, jclass type)
{
   //访问 Java 对象的静态属性,

   jfieldID fid = (*env)->GetStaticFieldID(env, type, "staticProp", "I");
   jint staticProp = (*env)->GetStaticIntField(env, type, fid);
   staticProp += 101;
   (*env)->SetStaticIntField(env, type, fid, staticProp);

}

调用 Java 对象的静态方法

JNIEXPORT void JNICALL
Java_com_haohao_hellojni_NativeUtils_callStaticJavaMethod(JNIEnv *env, jclass type)
{
   //调用 Java 对象的静态方法

   jmethodID mid = (*env)->GetStaticMethodID(env, type, "getStringFromJavaStatic", "(Ljava/lang/String;)Ljava/lang/String;");
   jstring jstr = (*env)->CallStaticObjectMethod(env, type, mid, (*env)->NewStringUTF(env, "Hello Java, I am From C."));
   const char* cstr = (*env)->GetStringUTFChars(env, jstr, NULL);
   LOGD("%s", cstr);

   //释放资源
   (*env)->ReleaseStringUTFChars(env, jstr, cstr);
}

测试

nativeUtils = new NativeUtils();
Log.i(TAG, "old prop : " + nativeUtils.prop);
nativeUtils.accessJavaClassProp();
Log.i(TAG, "new prop : " + nativeUtils.prop);
Log.i(TAG, "old static prop : " + NativeUtils.staticProp);
NativeUtils.accessStaticJavaProp();
Log.i(TAG, "new static prop : " + NativeUtils.staticProp);
nativeUtils.callJavaClassMethod();
NativeUtils.callStaticJavaMethod();

native-utils.c 完整代码

//
// Created by haohao on 2018/1/1.
//

#include <jni.h>
#include <android/log.h>

#define TAG "native-utils"
#define LOGD(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGD类型

JNIEXPORT void JNICALL
Java_com_haohao_hellojni_NativeUtils_accessJavaClassProp(JNIEnv *env, jobject instance)
{
   //访问 Java 对象的非静态属性

   //通过 JNIEnv 和对象 instance 实例拿到 class 。
   jclass cls = (*env)->GetObjectClass(env, instance);
   //获取属性的 field id
   jfieldID fid = (*env)->GetFieldID(env, cls, "prop", "I");
   //通过 field id 获取属性的值
   jint prop = (*env)->GetIntField(env, instance, fid);
   //在 Native 层修改属性
   prop += 101;
   (*env)->SetIntField(env, instance, fid, prop);

}

JNIEXPORT void JNICALL
Java_com_haohao_hellojni_NativeUtils_callJavaClassMethod(JNIEnv *env, jobject instance)
{
   //调用 Java 对象的非静态方法

   jclass myClass = (*env)->GetObjectClass(env, instance);
   //获取发方法的 method id
   jmethodID mid = (*env)->GetMethodID(env, myClass, "getStringFromJava", "(Ljava/lang/String;)Ljava/lang/String;");
   //调用 Java 对象的方法
   jstring  jstr = (*env)->CallObjectMethod(env, instance, mid, (*env)->NewStringUTF(env, "Hello Java, I am From C."));
   //jstring 转换为 c 的字符串
   const char* cstr = (*env)->GetStringUTFChars(env, jstr, NULL);
   LOGD("%s", cstr);
   //注意区别对待 Java 字符串和 C 的字符串,除了基本数据类型之外,其他都需要进行类型转换
   //释放资源
   (*env)->ReleaseStringUTFChars(env, jstr, cstr);

}

JNIEXPORT void JNICALL
Java_com_haohao_hellojni_NativeUtils_accessStaticJavaProp(JNIEnv *env, jclass type)
{
   //访问 Java 对象的静态属性,

   jfieldID fid = (*env)->GetStaticFieldID(env, type, "staticProp", "I");
   jint staticProp = (*env)->GetStaticIntField(env, type, fid);
   staticProp += 101;
   (*env)->SetStaticIntField(env, type, fid, staticProp);

}

JNIEXPORT void JNICALL
Java_com_haohao_hellojni_NativeUtils_callStaticJavaMethod(JNIEnv *env, jclass type)
{
   //调用 Java 对象的静态方法

   jmethodID mid = (*env)->GetStaticMethodID(env, type, "getStringFromJavaStatic", "(Ljava/lang/String;)Ljava/lang/String;");
   jstring jstr = (*env)->CallStaticObjectMethod(env, type, mid, (*env)->NewStringUTF(env, "Hello Java, I am From C."));
   const char* cstr = (*env)->GetStringUTFChars(env, jstr, NULL);
   LOGD("%s", cstr);

   //释放资源
   (*env)->ReleaseStringUTFChars(env, jstr, cstr);
}

结果

I/MainActivity: old prop : -1
I/MainActivity: new prop : 100
I/MainActivity: old static prop : -1
I/MainActivity: new static prop : 100
I/NativeUtils: getStringFromJava: Hello Java, I am From C.
I/native-utils: Hello C , I am from Java.
I/NativeUtils: getStringFromJavaStatic: Hello Java, I am From C.
I/native-utils: Hello C , I am from Java static.


微信公众号回复"001"或"666"或"999"领取知识星球活动优惠券!欢迎加入知识星球获取更多干货资源!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存