JNI与NDK基础入门

一、简介

1. 什么是JNI

JNi就是java调用本地方法的技术,最简单的来说,java运行一个程序需要要和不同的系统平台打交道,在windows里就是和windows平台底层打交道,mac就是要和mac打交道,jvm就是通过大量的jni技术使得java能够在不同平台上运行。使用了这技术的一个标志就是native,如果一个类里的一个方法被native修饰,那就说明这个方法是jni来实现的,他是通过本地系统api里的方法来实现的。当然这个本地方法可能是c或者C++,当然也可能是别的语言。jni是java跨平台的基础,jvm通过在不同系统上调用不同的本地方法使得jvm可以在不同平台间移植。
JNI UML
上图可以看到,JNI相当于是一层翻译器,JAVA想要调用本地方法,通过JNI解释,执行,同样的,本地方法想要调用JAVA方法,也是通过JNI来进行的。

2. JNI定义

  • 定义:Java Native Interface,即JAVA本地接口
  • 作用:使JAVA语言和其他编程语言(C、C++等)进行交互
  • JNI是JAVA调用NATIVE语言的一种特性
  • 实际中驱动都是C和C++开发的,通过JNI,JAVA可以调用C/C++实现的驱动,从而扩展JAVA虚拟机的能力。另外,在高效率的数学运算、游戏的实时渲染、音视频的编码和解码等方面,一般都是用C开发的

二、NDK是什么

1.定义

  • 定义:Native Development Kit,是android的一个开发工具包
  • 作用:快速开发C、C++的动态库,并自动将so和应用一起打包成APK
  • 提供了把.so和.apk打包的工具
  • NDK提供的库有限,仅拥有算法效率和敏感问题
  • 提供了交叉编译器,用于生成特定的CPU平台动态库

2.特点

  • 运行效率高
  • 代码安全性高
  • 功能扩展性好
  • 易于代码复用和移植

三、JNI和NDK的关系

1. NDK是Android中实现JNI的手段

JNI是实现JAVA调用C/C++的途径,NDK是android中调用本地方法的桥梁。所以可以说NDK是Android中实现JNI的手段, 即在Android的开发环境中,通过NDK从而实现JNI功能。 JAVA的优点是跨平台,和操作系统之间的调用由JVM完成,但是一些和操作系统相关的操作就无法完成,JNI的出现刚刚弥补了这个缺陷,也完善了JAVA语言,将JAVA扩展的更强大。

我们需要了解的是,每个平台编译的文件后缀是不一样的

平台后缀
android、linux.so
windows.dll
mac.a

四、JNI动态注册和静态注册

为了不让大家枯燥的看这些概念,我们通过两个例子来了解静态注册和动态注册。(我们在windows中运行的,所以下面例子中生成的库文件是.dll类型的)

1. 静态注册

点击这里查看具体使用方法

  • 在Java中声明Native方法(即需要调用的本地方法)
  • 编译上述JAVA源文件javac(得到.class文件)
  • 通过javah命令导出JNI的头文件(.h文件)
  • 使用JAVA需要交互的本地代码,实现在JAVA中声明的Native方法
  • 编译.so库文件
  • 通过JAVA命令执行JAVA程序,最终实现JAVA调用本地代码

2. 动态注册

在此之前我们一直在jni中使用的 Java_PACKAGENAME_CLASSNAME_METHODNAME 来进行与java方法的匹配,这种方式我们称之为静态注册。
而动态注册则意味着方法名可以不用这么长了,我们也不必再通过javah命令生成头文件,在android aosp源码中就大量的使用了动态注册的形式

//Java:
native void dynamicNative();
native String dynamicNative(int i);

//C++:
void dynamicNative1(JNIEnv *env, jobject jobj){
	LOGE("dynamicNative1 动态注册");
}
jstring dynamicNative2(JNIEnv *env, jobject jobj,jint i){
	return env->NewStringUTF("我是动态注册的dynamicNative2方法");
}
//需要动态注册的方法数组
static const JNINativeMethod mMethods[] = {
	{"dynamicNative","()V", (void *)dynamicNative1},
	{"dynamicNative", "(I)Ljava/lang/String;", (jstring *)dynamicNative2}
};

//需要动态注册native方法的类名
static const char* mClassName = "com/dongnao/jnitest/MainActivity";
jint JNI_OnLoad(JavaVM* vm, void* reserved){
	JNIEnv* env = NULL;
	//获得 JniEnv
	int r = vm->GetEnv((void**) &env, JNI_VERSION_1_4);
	if( r != JNI_OK){
		return -1;
	}
	jclass mainActivityCls = env->FindClass( mClassName);
	// 注册 如果小于0则注册失败
	r = env->RegisterNatives(mainActivityCls,mMethods,2);
	if(r != JNI_OK )
	{
		return -1;
	}
	return JNI_VERSION_1_4;
}

3. 在IDE中调用本地库的时候,需要指定java.library.path属性

  • 点击右上角的Edit Configuration
    在这里插入图片描述
  • 在Configuration标签页中,将VM options项中的-Djava.library.path属性指定到你的dll库目录下:
    在这里插入图片描述

4. 动态注册,我们需要指定java方法的方法名,方法签名,来与C函数建立关联,java方法签名是怎么获取的呢?

下面这张表对应了java方法返回值的签名:
在这里插入图片描述

我们也可以通过命令来查看:

使用javap命令:
javap -s -p JniTes.class

E:\java\JniTest1\src>javap -s -p Register
Compiled from "Register.java"
public class Register {
  public Register();
    descriptor: ()V

  public native java.lang.String HelloWorld();
    descriptor: ()Ljava/lang/String;
}

上面是终端打印出来的, 其中:
()V,代表的是Register的构造函数,是Void类型的;
()Ljava/lang/String; 是我们的本地方法,类型是String。
特别还要注意的是String后面的分号,千万不要忘记了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿烦大大@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值