JNI和NDK编程-JNI的开发流程,jnindk-jni流程
分享于 点击 32062 次 点评:231
JNI和NDK编程-JNI的开发流程,jnindk-jni流程
Java JNI的本意时Java Native Interface(Java本地接口),它是为了方便Java调用C、C++等本地代码所封装的一层接口。我们都知道,Java的有点是跨平台,但是作为有点的同事,其在和本地交互的时候出现了短板。Java的跨平台特性导致其本地交互的能力不够强大,一些和操作系统相关的特性Java无法完成,于是Java提供了JNI专门用于和本地代码交互,这一就增强Java语言的本地交互能力。通过Java JNI,用户可以调用C、C++所编写的本地代码。NDK是Android所提供的一个工具集合,通过NDK可以在Android中更加方便地通过JNI来访问本地代码,比如C或者C++。NDK还提供了交叉编译器,开发人员只需要简单地修改mk文件就可以生成特定CPU平台地动态库。使用NDK有如下好处:(1)提高代码的安全性。由于so库反编译比较困难,因此NDK提高了Android程序的安全性。(2)可以很方便地使用目前已有地C/C++实现的动态库可以很方便地在其他平台上使用。(3)便于平台间地移植。通过C/C++实现地动态库可以很方便地在其他平台使用。(4)提高程序在某些特定情形下地执行效率,但并不能明显提示Android程序的性能。
由于JNI和NDK比较适合,JNI和NDK开发用到的动态库的格式是以.so为后缀的文件,下面统一简称为so库。另外,由于JNI和NDK主要用于底层和嵌入式开发,在Android的应用开发中使用较少,加上他们本身更加侧重于C和C++方面的变成,因此本章只介绍JNI和NDK的基础知识,其他更加深入的知识点如果感兴趣的话可以专门查看JNI和NDK的书籍。
下面进入本节正题,JNI开发流程有如下几部,首先需要在Java中声明native方法,接着用C或者C++实现native方法,然后就可以编译运行了。
1.在Java中声明native方法。
创建一个类,这里叫做JniTest.java,代码如下所示。public class JniTest {
static {
System.loadLibrary("jin-test");
}
public native String get();
public native void set(String str);
}
可以看到上面的代码中,声明了两个native方法:get和set(String),这两个就是需要在JNI中实现的方法。在JniTest的头部有一个加载动态库的过程,其中jni_test是so库的标识,so库完整的名称为libjni-test.so,这是加载so库的规范。2.使用javah命令导出JNI的头文件
我这里使用的是Android Studio,生成.h文件的方法如下:打开AS的terminal,进入到main/java路径,键入以下命令生成.h文件javah study.chenj.testlibrary.JniTest
在AS中可以看到生成的.h文件“study_chenj_testlibrary_JniTest.h”,内容如下所示。/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class study_chenj_testlibrary_JniTest */
#ifndef _Included_study_chenj_testlibrary_JniTest
#define _Included_study_chenj_testlibrary_JniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: study_chenj_testlibrary_JniTest
* Method: get
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_study_chenj_testlibrary_JniTest_get
(JNIEnv *, jclass);
/*
* Class: study_chenj_testlibrary_JniTest
* Method: set
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_study_chenj_testlibrary_JniTest_set
(JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif
上面的代码需要做一下说明,首先函数名的格式遵循如下规则:Java_包名_类名_方法名。比如JniTest中的set方法,到这里就变成了JNIEXPORT void JNICALL Java_study_chenj_testlibrary_JniTest_set(JNIEnv *, jclass, jstring),其中study.chenj.testlibrary是包名,JniTest是类名,jstring是代表的是set方法的String类型的参数。关于java和JNI的数据类型直接的对应关系会在下面进行介绍,这里只需要知道java的String对应与JNI的jstring即可。JNIEXPORT、JNICALL、JNIEnv和jonject都是JNI标准中所定义的类型或者宏,他们的含义如下:(1)JNIEnv*:表示一个指向JNI环境的指针,可以通过它来访问JNI提供的接口方法;(2)jobject:表示java对象中的this;(3)JNIEXPORT和JNICALL:它是JNI中所定义的宏,可以在jni.h这个头文件中查找到。下面的宏定义是必需的,它指定extern “C”内部的函数采用C语言的命名风格来编译。否则当JNI采用C++来实现时,由于C和C++编译过程中对函数的命名风格不同,这将导致JNI在链接时无法根据函数名查找到具体函数,那么JNI调用就无法完成。更多的细节实际上是有关于C和C++编译时的一些问题,这里就不再展开了。#ifdef __cplusplus
extern "C" {
#endif
3.实现JNI方法
JNI方法是指Java中声明的native方法,这里可以选择用C++或者C来实现,他们的实现过程是类似的,只有少量的区别,下面是分别用C++和C来实现JNI方法。首先,在工程的主目录下创建一个子项目,名称随意,这里选择jni作为目录的名称,然后将之前通过javah生成的头名叫“study_chenj_testlibrary_JniTest.h”复制到jni目录下,接着创建test.cpp和test.c两个文件,他们的实现如下所示。//test.cpp
#include "study_chenj_testlibrary_JniTest.h"
#include <stdio.h>
JNIEXPORT jstring JNICALL Java_study_chenj_testlibrary_JniTest_get(JNIEnv *, jobject thiz){
printf("invoke get in c++ \n");
return env->NewStringUTF("Hello form JNI!");
}
JNIEXPORT void JNICALL Java_study_chenj_testlibrary_JniTest_set(JNIEnv *, jobject thiz, jclass, jstring string){
printf("invoke set in c++ \n");
char *str = (char *)env->GetStringUTFChars(string,NULL);
printf("%s",str);
env->ReleaseStringUTFChars(string, str);
}
//test.c
#include "study_chenj_testlibrary_JniTest.h"
#include <stdio.h>
JNIEXPORT jstring JNICALL Java_study_chenj_testlibrary_JniTest_get(JNIEnv *, jobject thiz){
printf("invoke get in c \n");
return (*env)->NewStringUTF(env, "Hello form JNI!");
}
JNIEXPORT void JNICALL Java_study_chenj_testlibrary_JniTest_set(JNIEnv *, jobject thiz, jclass, jstring){
printf("invoke set in c \n");
char *str = (char *)(*env)->GetStringUTFChars(env, string,NULL);
printf("%s",str);
(*env)->ReleaseStringUTFChars(env, string, str);
}
可以发现,test.cpp和test.c的实现很类似,但是他们对env的操作方式有所不同,因此用C++和C来实现JNI方法,他们的区别主要集中在对env的操作上,其他都是类似的,如下所示。C++:env->NewStringUTF("Hello form JNI!")
C: (*env)->NewStringUTF(env, "Hello form JNI!")
相关文章
- 暂无相关文章
用户点评