NDK-Java与native的互相调用

/ 0评 / 0

前言

http://27house.cn/archives/1097中,我介绍了下了如何编写一个简单的Demo来让native输出hello world到java层,接下来本篇主要介绍Java层调用native方法,以及native层调用Java层的方法以及修改Java对象的属性。

Java层调用native层的方法

其实在上一篇中已经介绍了下如何调用native层方法,其实与调用普通的Java方法没有什么区别,只是方法由native层实现的而已。

首先我们在Java类中编写native方法,然后使用上一篇博客中介绍的方法在native层实现之。

package org.ndk.ndkfirst;

public class NDKTest {

    private static final String TAG = "NDKTest";

    static {
        System.loadLibrary("ndk-test-lib");
    }

    private String mString = "Ndk test string";

    /**
     * 在native中进行加法运算
     */
    public native int calcInNative(int num1, int num2);
}
#include <jni.h>

extern "C"
{
	JNIEXPORT jint JNICALL
	Java_org_ndk_ndkfirst_NDKTest_calcInNative(JNIEnv *env, jobject obj, jint num1, jint num2) {
		return num1 + num2;
	}
}

我们可以看到,只是将加法运算在native层实现而已,调用native方法与普通Java方法一样的,调用代码如下。

NDKTest nDKTest = new NDKTest();
nDKTest.calcInNative(1, 2);

native层调用Java方法

Java数据类型的对应

在native层调用Java方法,首先得知道Java类型与native的对应关系,以及Java类型以及方法在native中的签名格式。

对于基础类型,对应关系如下。这些数据类型的定义全在jni.h中

#define JNI_FALSE   0
#define JNI_TRUE    1
typedef unsigned char   jboolean;       /* unsigned 8 bits */
typedef signed char     jbyte;          /* signed 8 bits */
typedef unsigned short  jchar;          /* unsigned 16 bits */
typedef short           jshort;         /* signed 16 bits */
typedef int             jint;           /* signed 32 bits */
typedef long long       jlong;          /* signed 64 bits */
typedef float           jfloat;         /* 32-bit IEEE 754 */
typedef double          jdouble;        /* 64-bit IEEE 754 */

对于非基础类型,比如String等,Array等统一对应jobject对象。

class _jobject {};
class _jclass : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jobjectArray : public _jarray {};
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 _jthrowable : public _jobject {};

typedef _jobject*       jobject;
typedef _jclass*        jclass;
typedef _jstring*       jstring;
typedef _jarray*        jarray;
typedef _jobjectArray*  jobjectArray;
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 _jthrowable*    jthrowable;
typedef _jobject*       jweak;

Java类型以及方法签名

在native中调用Java层的方法以及属性的时候需要使用相应的签名表示方法以及属性,下面就来介绍下基础类型与引用类型的签名表示方法。

从上面的表可以看到,基础类型是使用一个大写字母表示,对于数据则是使用[数组类型签名,比如int[]数组的签名为[I,一维数组是一个[,多维数组就是多个[[[,对于Java中的对象则是L类全路径;,不要忘记结尾的;号,对于对于函数方法则是(参数一签名参数二签名...)返回值签名,中间没有空格。

比如上面的 public native int calcInNative(int num1, int num2);方法的前面为(II)I,其他的类似。

native调用Java中的方法

首先我们得知道,任何jni的方法都需要通过JNIEnv *env参数去调用

//C++形式
env->方法名
//C形式
(*env)->方法名

native层调用Java层方法以及属性的时候类似于使用反射去调用Java的方法,基本分为如下几步。

1、获取jclass对象(类似于Java中的Class对象)

//方法一,通过jobject对象直接获取
jclass jclass1 = env->GetObjectClass(jobject);
//方法二,通过类描述符获取
//匿名类使用$号隔开,比如Build中定义的VERSION->android/os/Build$VERSION
env->FindClass(类描述符);
//这里不是Ljava/lang/String;的形式,只有在参数列表中需要这么写
jclass cls = env->FindClass("java/lang/String");

2、获取jmethodID或者jfieldID(类似于Java中的Method或者Field)

//获取非静态方法
//参数const char *name 方法名
//参数const char *sig 方法签名
jmethodID env->GetMethodID(jclass clazz, const char *name, const char *sig)
//获取静态方法
jmethodID env->GetStaticMethodID(jclass clazz, const char *name, const char *sig)

//获取非静态的成员变量
jfieldID env->GetFieldID(jclass clazz, const char *name, const char *sig)

//获取静态的成员变量
jfieldID env->GetStaticFieldID(jclass clazz, const char *name, const char *sig)

//获取指定类型的成员变量,如下面一个
jboolean env->Get[Type]Field(jobject obj, jfieldID fieldID)
jboolean env->GetBooleanField(jobject obj, jfieldID fieldID)

//获取指定类型的静态成员变量,如下面一个
jboolean env->GetStatic[Type]Field(jclass clazz, jfieldID fieldID)
jboolean env->GetStaticBooleanField(jclass clazz, jfieldID fieldID)

3、调用相应函数以及获取/修改属性值

//调用Java方法
env->Call[Type]Method(...)
jobject env->CallBooleanMethod(jobject obj, jmethodID methodID, ...)
// 调用Java静态方法
env->CallStatic[Type]Method(...)
jboolean env->CallStaticBooleanMethod(jclass clazz, jmethodID methodID, ...)

//获取Java属性值
env->Get[Type]Field()
jint env->GetIntField(jobject obj, jfieldID fieldID)
//获取Java静态属性值
env->GetStatic[Type]Field()
jboolean env->GetStaticBooleanField(jclass clazz, jfieldID fieldID)

//设置Java属性值
env->Set[Type]Field()
void env->SetBooleanField(jobject obj, jfieldID fieldID, jboolean value)
//设置Java静态属性值
env->SetStatic[Type]Field()
void env->SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value)

native调用举例

由于本篇博客篇幅已经较长,所以这里只给出部分关键代码。下面为Java层代码。

package org.ndk.ndkfirst;

import android.util.Log;

public class NDKTest {

    private static final String TAG = "NDKTest";

    static {
        System.loadLibrary("ndk-test-lib");
    }

    private String mString = "Ndk test string";

    /**
     * 在native中调用java方法
     */
    public native void callJavaMetood();

    /**
     * 在native中修改对象的字段
     */
    public native void modifyFiled();

	/**
	 * 输出成员变量的值
	 */
    public void outputString() {
        Log.i(TAG, "mString: " + mString);
    }

	/**
	 * 在native中调用此方法
	 */
    @Override
    public String toString() {
        Log.i(TAG, "toString: ");
        return mString;
    }
}

对应的native层实现。

#include <jni.h>

extern "C"
{
	JNIEXPORT void JNICALL Java_org_ndk_ndkfirst_NDKTest_callJavaMetood(JNIEnv *env, jobject obj) {
		jclass jclass1 = env->GetObjectClass(obj);
		jmethodID methodID = env->GetMethodID(jclass1, "toString", "()Ljava/lang/String;");
		env->CallObjectMethod(obj, methodID);
	}

	JNIEXPORT void JNICALL Java_org_ndk_ndkfirst_NDKTest_modifyFiled(JNIEnv *env, jobject obj) {
		jclass jclass1 = env->GetObjectClass(obj);
		jfieldID fieldID1 = env->GetFieldID(jclass1,"mString","Ljava/lang/String;");
		env->SetObjectField(obj,fieldID1,env->NewStringUTF("native modify it"));
	}
}

Demo地址:github

发表评论

您的电子邮箱地址不会被公开。