热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

[置顶]Android本地接口JNI的使用分析

本地接口JNI的使用分析我们知道,在AndroidFramework中,其是由基于Java语言的Java层和基于CC++语言的CC++层所组成,那么他们之间是怎样

本地接口JNI的使用分析

我们知道,在Android Framework中,其是由基于Java语言的Java层和基于C/C++语言的C/C++层所组成,那么他们之间是怎样沟通的那?在Android中,其提供了一种中间的媒介或是桥梁,能很好的将Java层和C/C++层联系起来,使得他们互相协调,共同完成功能任务。在这两层之间充当桥梁的即是JNI了,我们称之为“Java本地接口”(Java Native Interface),它允许Java代码编写的应用程序能够与使用C/C++编写生成的库(.so/.dll)进行交互操作。

这就是本篇文章要介绍的JNI本地接口了,同时,在这里我会介绍下由谷歌提供的NDK工具,它是用来快速编写基于JNI机制的应用的一套工具集。好了,下面逐一介绍下。

JNI的原理:

 

如上图所示,JNI的工作机制是这样的,当我们准备好了Java的本地方法和对应的函数原型之后,Java虚拟机在加载动态库的时候先检查动态库中是否含有与本地方法同签名的方法,如果有的话,其会将Java本地方法和对应的函数原型进行映射,这样我们就可以在JNI中调用本地方法来访问底层驱动了。如果没有的话,则会抛出异常,停止工作。

JNI的使用:

接下来,介绍下JNI的使用,它的使用分为以下几个步骤:

1、编写一个Java本地方法

2、编译上面Java本地方法

A、使用Java编译器 即使用命令javac来编译 生成对应的class文件

B、运行当前的项目 会自动生成对应的class文件 

依个人兴趣 选择即可 我使用的第二种 因为方便

3、生成C/C++头文件

在这里使用Java编译器命令javah来生成对应上面的class文件的C/C++头文件

我的系统是windows 所以可以cd到class文件的目录 使用javah编译生成需求的头文件

4、编写C/C++代码

A、将C/C++头文件代码复制到对应名称的.c文件(这里使用C编写)

B、按照头文件中的规则编写实现文件即可

5、生成动态共享库(.so)

A、使用C/C++开发工具 如visual c++ 或visual studio等工具来生成库文件

B、我们也可以使用辅助工具Cygwin来编译生成库文件

6、运行Android程序

在这里,我会举个简单的例子来详细的介绍JNI的使用流程,具体如下所示,我的项目目录:

 

运行结果如下:

当我点击界面上的按钮,会调用Java本地方法,然后通过JNI将其与JNI的本地函数进行沟通打印LOG日志:

 

 

好了,接下来开始详细的介绍。

首先,先编写Java的本地方法,这个本地方法主要是为JNI的本地函数生成提供原型,我的代码:

public class VerifyJNIMethod {

static {

System.loadLibrary("hello_jni");

}

public static native String helloJni();

}

代码是很简单的,就是简单的打印LOG日志,在这里使用了静态块加载方式static,用意是当Android系统启动时就将本地的.so库加载到Java虚拟机中,以便调用及时不致发生错误。

另外,对于Java本地方法的方法是只有声明没有实现的,而且必须使用关键字native来声明,否则编译器会报错,这个关键字主要是告诉编译器当前修饰的方法为只有声明的方法,并且是有C/C++语言来实现的。

其次,我们要编译上面的Java本地方法,可以使用编译器命令javac来编译(前提注意JDK的环境变量是否设定正确),当然也可以直接运行项目程序(这是的本地方法不要调用JNI,因为其不识别),会自动生成本地方法的class文件。

接下来,就是编译.class文件生成对应的C/C++头文件了,我们只需要使用编译器命令javah即可自动生成头文件,具体如下:

因为我的电脑是windows的,所以打开命令窗口,cd到我们的项目目录下,执行命令:javah -classpath bin/classes -d jni com.demo.verifyjni.jni.VerifyJNIMethod在项目根目录下新建一个jni目录来存放生成的头文件,当然后面编写的实现文件和Android.mk文件也放在这里吧!这是自动生成的头文件为com_demo_verifyjni_jni_VerifyJNIMethod.h(命名规范:包名_类名),这个名字有点过长,我们将其修改为VerifyJNIMethod.h使其与下面的实现文件名称一致,便于理解。

 

我的头文件如下:

/* DO NOT EDIT THIS FILE - it is machine generated */

#include 

/* Header for class com_demo_verifyjni_jni_VerifyJNIMethod */

 

#ifndef _Included_com_demo_verifyjni_jni_VerifyJNIMethod

#define _Included_com_demo_verifyjni_jni_VerifyJNIMethod

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:     com_demo_verifyjni_jni_VerifyJNIMethod

 * Method:    helloJni

 * Signature: ()Ljava/lang/String;

 */

JNIEXPORT jstring JNICALL Java_com_demo_verifyjni_jni_VerifyJNIMethod_helloJni

  (JNIEnv *, jclass);

 

#ifdef __cplusplus

}

#endif

#endif

如果是第一次看到这个文件,会认为很复杂特别是对C不熟悉的读者,但静下心来看其实很简单,分析如下:

在这里我们只需要关注函数原型部分,也就是JNIEXPORT jstring JNICALL Java_com_demo_verifyjni_jni_VerifyJNIMethod_helloJni

  (JNIEnv *, jclass);

部分,这里的函数原型的规则是这样的:JNIEXPORT+JNI数据类型+JNICALL+Java_包名_类名_方法名,里面的JNICALL、JNIEXPORT都是JNI的关键字,表示此函数要被JNI调用的,另外方法里面的参数为共同参数,其中的JNIEnv *参数是JNI接口的指针,用来调用JNI表中各种JNI函数(非JNI的本地函数),来创建或调用Java对象用的指针类型。第二个参数jclass则为如果Java的本地方法为静态方法的话,那么就会出现这个参数,否则参数类型为jobject,jclass代表着JNI中的类与Java中的类的对应类型,而jobject则为对象类型。

 

好了,头文件已经编译成功了,接下来就是头文件的实现文件的编写了。实现文件的编写推荐根据头文件规范编写,具体如下:

#include 

#include "VerifyJNIMethod.h"

 

JNIEXPORT jstring JNICALL Java_com_demo_verifyjni_jni_VerifyJNIMethod_helloJni

  (JNIEnv *env, jclass obj)

{

return (*env)->NewStringUTF(env,"Hello-Jni!"); 

}

这个文件当中使用了函数NewStringUTF(),它则代表着JNI函数,用来将C的字符创转换为Java的字符串类型并返回。返回之后,就会在Android前段接收返回的内容并打印LOG日志。

注意:JNI提供了多种JNI函数,用来处理C/C++与Java之间的转化操作,具体看查看地址:http://java.sun.com/docs/books/jni

到这里,上面已经准备好了Java本地方法、对应的C/C++的头文件和实现文件,那么就剩下了怎么生成C的共享库文件了。这个也比较简单,我们可以使用辅助工具Cygwin(下载地址:http://www.cygwin.com)来编译生成库文件,但必须注意一点必须编写Android.mk文件并配置,这样才会变异通过,我的mk文件如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := hello_jni

LOCAL_SRC_FILES := VerifyJNIMethod.c

include $(BUILD_SHARED_LIBRARY)

对于这个文件会在下面的NDK中介绍。依我的习惯是将头文件、实现文件及mk文件放到项目外的jni目录下,然后打开Cygwin软件cd到目录:/cygdrive/jni/jni,执行命令$NDK/ndk-build回车即可生成需要的.so文件了,我的如下:

 

好了,最后就剩下了怎么在前端使用Java调用JNI的方法来打印LOG了,我的实现代码如下:

public class VerifyJNIActivity extends Activity {

private static String TAG = "VerifyJNIActivity";

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_verify_jni);

}

public void helloJNI(View v) {

String result = VerifyJNIMethod.helloJni();

log(result);

}

private void log(String log) {

Log.d(TAG, log);

}

}

代码简单至极,调用VerifyJNIMethod.helloJni()打印LOG日志即可,没当点击下按钮就会打印”Hello-Jni!”了,效果在上面已经列出了。接下来来介绍下谷歌提供的NDK工具来快捷开发基于JNI的C/C++的代码与Java交互了。

注意:在最后为了减小APK包的大小,我们会将根目录下的jni目录删除掉,它只是为演示而已。

 

 

 

 

/**

* 欢迎加入技术群:179914858

* 如果有任何问题请在评论中进行讨论学习

*/

 



推荐阅读
  • 本文详细介绍了Java中org.neo4j.helpers.collection.Iterators.single()方法的功能、使用场景及代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • Java 中 Writer flush()方法,示例 ... [详细]
  • 本文介绍了如何使用 Spring Boot DevTools 实现应用程序在开发过程中自动重启。这一特性显著提高了开发效率,特别是在集成开发环境(IDE)中工作时,能够提供快速的反馈循环。默认情况下,DevTools 会监控类路径上的文件变化,并根据需要触发应用重启。 ... [详细]
  • Java 中的 BigDecimal pow()方法,示例 ... [详细]
  • Java 类成员初始化顺序与数组创建
    本文探讨了Java中类成员的初始化顺序、静态引入、可变参数以及finalize方法的应用。通过具体的代码示例,详细解释了这些概念及其在实际编程中的使用。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 主要用了2个类来实现的,话不多说,直接看运行结果,然后在奉上源代码1.Index.javaimportjava.awt.Color;im ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 本文详细介绍了macOS系统的核心组件,包括如何管理其安全特性——系统完整性保护(SIP),并探讨了不同版本的更新亮点。对于使用macOS系统的用户来说,了解这些信息有助于更好地管理和优化系统性能。 ... [详细]
  • 本文探讨了 C++ 中普通数组和标准库类型 vector 的初始化方法。普通数组具有固定长度,而 vector 是一种可扩展的容器,允许动态调整大小。文章详细介绍了不同初始化方式及其应用场景,并提供了代码示例以加深理解。 ... [详细]
author-avatar
Li_pengwei
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有