Android JNI与NDK(一)

JNI是Java Native Interface(Java本地接口),它是为了方便Java调用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开发所用到的动态库的格式是以.so为后缀的文件。

JNI开发

1.在Java中声明native方法

public class JniTest {

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

    public static void main(String[] args) {
        JniTest jniTest = new JniTest();
        System.out.println(jniTest.get());
        jniTest.set("Hello Homeless");
    }

    public native String get();

    public native void set(String string);

}

2.生成class文件与h文件

javac JniTest.java
javah -classpath . -jni com.xupt.will.jnitest.JniTest

在命令下会生成一个文件。
在这里插入图片描述
内容如下:

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

#ifndef _Included_com_xupt_will_jnitest_JniTest
#define _Included_com_xupt_will_jnitest_JniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_xupt_will_jnitest_JniTest
 * Method:    get
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_xupt_will_jnitest_JniTest_get
  (JNIEnv *, jobject);

/*
 * Class:     com_xupt_will_jnitest_JniTest
 * Method:    set
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_xupt_will_jnitest_JniTest_set
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

函数名格式:Java_包名_类名_方法名
JNIEnv*:表示一个指向JNI环境的指针,可以通过它来访问JNI提供的接口方法。
JNIEXPORT和JNICALL:它们是JNI所定义的宏,在jni,h这个头文件中可以找到。
extern C :它指定extern C内部的函数采用C语言的命名风格来编译。

3.实现JNI方法
在主目录下创建一个子目录,名称随意,一般选择jni作为这个子目录的名称。然后将生成的.h文件复制到这个子目录中,然后创建test.cpp和test.c两个文件。

test.cpp

#include "com_xupt_will_jnitest_JniTest.h"
#include <stdio.h>

JNIEXPORT jstring JNICALL Java_com_xupt_will_jnitest_JniTest_get(JNIEnv *env,jobject thiz){
	printf("invoke get from C++\n");
	return env->NewStringUTF("Hello from JNI !");
}

JNIEXPORT void JNICALL Java_com_xupt_will_jnitest_JniTest_set(JNIEnv *env,jobject thiz,jstring string){
	printf("invoke get from C++\n");
	char* str = (char*)env->GetStringUTFChars(string,NULL);
	printf("%s\n",str);
	env->ReleaseStringUTFChars(string,str);
}

test.c

#include "com_xupt_will_jnitest_JniTest.h"
#include <stdio.h>

JNIEXPORT jstring JNICALL Java_com_xupt_will_jnitest_JniTest_get(JNIEnv *env,jobject thiz){
	printf("invoke get from C\n");
	return (*env)->NewStringUTF(env,"Hello from JNI !");
}

JNIEXPORT void JNICALL Java_com_xupt_will_jnitest_JniTest_set(JNIEnv *env,jobject thiz,jstring string){
	printf("invoke get from C\n");
	char* str = (char*)(*env)->GetStringUTFChars(env,string,NULL);
	printf("%s\n",str);
	(*env)->ReleaseStringUTFChars(env,string,str);
}

可以看到,test.cpp与test.c的实现很类似,但是对env的操作有所不同,区别如下所示:

C++: env->NewStringUTF("Hello from JNI !");
C: (*env)->ReleaseStringUTFChars(env,string,str);

4.编译库并在Java中调用

//根据C++生成dll库
gcc -shared -I E:/JavaJDK/include -I E:/JavaJDK/include/win32 -fPIC test.cpp -o jni-test.dll		
//根据C生成dll库
gcc -shared -I E:/JavaJDK/include -I E:/JavaJDK/include/win32 -fPIC test.c -o jni-test.dll

这里生成so库时需要jni.h文件与jni_md.h文件,所以我们需要-I指出地址,分别在你Java路径的include目录下和include\win32目录下。还有需要注意的是,在windows平台下,这些地址要反斜杠,就是与你直接复制目录的斜杆相反,否则会找不到地址,因为JNI和NDK适合在Linux平台下做开发。

还需要注意的是在Linux及Android平台下,所需要的文件是so库,而在Windows平台下所需要的文件是dll。

5.运行

运行时我们需要指定dll文件所在的位置,否则会报出UnsatisfiedLinkError。我们将生成好的dll文件放在lib目录下,然后指定lib目录。

点击Run > Edit Configurations,然后再VM options中输入
-Djava.library.path=E:\IdeaProjects\JniTest\lib,即你存放dll文件的位置。

结果如下图所示:
C生成的dll文件
在这里插入图片描述
C++生成的dll文件
在这里插入图片描述
至此,在IDEA上,使用JNI大体步骤便完成了。

JNI的数据类型和类型签名。

JNI的数据类型包含两种:基本类型和引用类型。

基本类型

JNI类型Java类型描述
jbooleanboolean无符号8位整型
jbytebyte无符号8位整型
jcharchar无符号16位整型
jshortshort有符号16位整型
jintint32位整型
jlonglong64位整型
jfloatfloat32位浮点型
jdoubledouble64位浮点型
voidvoid无类型

引用类型

JNI类型Java类型描述
jobjectObjectObject类型
jclassClassClass类型
jstringStringString类型
jobjectArrayObject[]对象数组
jbooleanArrayboolean[]boolean数组
jbyteArraybyte[]byte数组
jcharArraychar[]char数组
jshortArrayshort[]short数组
jintArrayint[]int数组
jlongArraylong[]long数组
jfloatArrayfloat[]float数组
jdoubleArraydouble[]double数组
jthrowableThrowableThrowable

JNI的类型签名则标识了一个特定的Java类型,这个类型既可以是类和方法,也可以是数据类型。

类签名:它采用" L+包名+类型+; "的形式, 只需要将其中的.替换为/即可. 例如java.lang.String, 它的签名为Ljava/lang/String; , 注意末尾的;也是签名一部分。

基本数据签名:

Java类型签名Java类型签名Java类型签名
booleanZbyteBcharC
shortSintIlongJ
floatFdoubleDvoidV

数组签名:
[+类型签名, 例如byte数组. 其类型为byte, 而byte的签名为B, 所以byte数组的签名就是[B.同理可以得到如下的签名对应关系:

char[]      [C
float[]     [F
double[]    [D
long[]      [J
String[]    [Ljava/lang/String
Object[]    [Ljava/lang/Object

如果是多维数组那么就根据数组的维度多少来决定[的多少, 例如int[][]的签名就是[[I,其他情况可以依此类推.

方法签名:参数类型签名)+返回值类型签名。
举个例子, 如下方法: boolean fun1(int a, double b, int[] c). 参数类型的签名连在一起是ID[I, 返回值类型的签名为Z, 所以整个方法的签名就是(ID[I)Z。
例如方法:boolean fun1(int a, String s, int[] c), 那么签名就是(ILjava/lang/String;[I)Z。
为了更好的理解方法的签名格式, 下面再给出两个示例:

int fun1()        对应签名()I
int fun1(int i)   对应签名(I)I
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值