NDK 开发中 Native 与 Java 交互

人生远比我们想象得漫长,所以不争一城一池的得失,要把一切机会和挫折都放到尽可能长的周期里来思考。

本博客 NDK 开发系列文章:

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

Native 中的签名

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

属性签名

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

属性类型 类型签名
boolean Z
byte B
char C
short S
int I
long L
float F
double D
void V
String Ljava/lang/String;
Object 以 L 开头,后加完整的包名,并以分号结束
byte[] [B
Object[] 以 [ 开头,后加对象类型签名,例如 String[] 对应 [Ljava/lang/String;

方法签名

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

那么如何获取方法签名?
执行命令 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

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.
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,454评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,553评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,921评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,648评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,770评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,950评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,090评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,817评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,275评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,592评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,724评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,409评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,052评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,815评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,043评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,503评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,627评论 2 350

推荐阅读更多精彩内容