NDK-JNI与Java的交互 hello-world

/ 0评 / 0

前言

在前面NDK-CMake初探中我介绍了下Android Studio搭建NDK环境的步骤,当环境搭建起来以后,我们就可以开始编写native代码与Java层进行互相交互了,下面我就来简单的介绍下如何native与Java如何进行交互。

来自native层的hello world

编写Java层代码

使用native可以修饰一个native方法,表示这个函数在c/c++中实现,System.loadLibrary();用于加载so文件。

package org.ndk.ndkfirst;
public class NDKTest {

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

    /**
     * native返回一个字符串给Java层
     */
    public native String getStringFromNative();

}

编写相应的native代码

要想编写C/C++代码,我们首先得知道Java层的native修饰函数对应的C/C++表现形式。命名规则如下:

JNIEXPORT 返回值类型 JNICALL Java_包名_类名_函数名(JNIEnv * env, jobject obj)

上面例子中的getStringFromNative方法按照这个规则就是如下形式

JNIEXPORT jstring JNICALL Java_org_ndk_ndkfirst_NDKTest_getStringFromNative(JNIEnv * env, jobject obj);

JNIEXPORT JNICALL都是jni的关键字,表示此函数是要被jni调用的,而jstring为jni中定义的一个类型,对应Java中的String。

参数列表中的第一个参数JNIEnv* 是定义任意native函数的第一个参数(包括调用JNI的RegisterNatives函数注册的函数),指向JVM函数表的指针,函数表中的每一个入口指向一个JNI函数,每个函数用于访问JVM中特定的数据结构。JNIEnv是一个与线程相关的变量,不同线程的JNIEnv彼此独立。

第二个参数有一点特殊,对于普通函数(非static修饰),为jobject,表示当前的对象,如果是静态函数,为jclass,表示当前类。

通过上面的分析,我们可以写出如下的native代码:

#include <jni.h>

extern "C"
{
	JNIEXPORT jstring JNICALL
	Java_org_ndk_ndkfirst_NDKTest_getStringFromNative
			(JNIEnv *env, jobject obj) {
		//NewStringUTF表示创建一个utf-8格式的字符串
		return env->NewStringUTF("hello world from native~~~");
	}
}

接下来按照NDK-CMake初探中的介绍配置好CMakeLists.txt文件即可。

cmake_minimum_required(VERSION 3.4.1)
#设置生成的so动态库最后输出的路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})
add_library(ndk-test-lib
            SHARED
            src/main/cpp/ndk_test.cpp)
find_library(log-lib
             Specifies the name of the NDK library that
             log)
target_link_libraries(ndk-test-lib ${log-lib} )

调用JNI函数和调用普通函数没有什么区别。如下。

NDKTest mNDKTest = new NDKTest();
Log.i(TAG, mNDKTest.getStringFromNative());

使用javah命令生成头文件

在上面介绍了JNI函数的命名规则,下面就来介绍一个使用javah命令来生成所需要的函数形式的方法。javah命令格式

javah -classpath [path1] -jni [path2]

首先介绍下path2参数,path2为含有的native方法的完整类路径,对于上面的例子,就是如下形式:

org.ndk.ndkfirst.NDKTest

path1需要指定path2完整类路径的绝对路径。比如上面的NDKTest.java文件在C:\NDKFirst\app\src\main\java\org\ndk\ndkfirst\NDKTest.java下面,那么path1就应该为C:\NDKFirst\app\src\main\java

所以最后的命令如下,最后生成的.h文件在C:\NDKFirst\app\src\main\java下,里面就有我们需要的JNI函数,copy一下就行了。

javah -classpath "C:\Users\Jay\Desktop\NDKFirst\app\src\main\java" -jni org.ndk.ndkfirst.NDKTest
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_ndk_ndkfirst_NDKTest */

#ifndef _Included_org_ndk_ndkfirst_NDKTest
#define _Included_org_ndk_ndkfirst_NDKTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     org_ndk_ndkfirst_NDKTest
 * Method:    getStringFromNative
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_org_ndk_ndkfirst_NDKTest_getStringFromNative
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

Demo地址:github

发表评论

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