百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

什么是JNI?为什么会有Native层?如何使用?

ztj100 2024-12-06 20:56 13 浏览 0 评论

什么是JNI?

JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植;从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互

本地代码与 Java 虚拟机之间是通过 JNI 函数实现相互操作的;JNI 函数通过接口指针来获得,本地方法将 JNI 接口指针当作参数来接受;虚拟机保证在从相同的 Java 线程中对本地方法进行多次调用时,传递给本地方法的接口指针是相同的,本地方法被不同的 Java 线程调用时,它接受不同的 JNI接口指针

使用JNI和算法进行交互,主要是为了提高算法的性能,最大化的利用机器硬件资源

Android 中的语言层

可以将语言层分成Java层和Native层;为什么会有Native层呢?是因为在Java出现之前,很多的功能和系统都是由Native语言写的,所以Java出现之后就不必重复造轮子,直接调用这些方法就行了,而且Native层更接近汇编语言,所以性能更优

所以代码中就出现了Java层调用Native层的方法,而这个跨语言的调用过程就是 JNI

当Java语言无法处理一些任务的时候,就可以使用JNI来完成

下面是几个 JNI 的应用场景:

需要调用Java语言不支持的依赖于操作系统平台特性的一些功能

● 需要调用当前UNIX系统的某个功能,而Java不支持这个功能的时候,就要用到JNI

● 在程序对时间敏感或对性能要求特别高时,有必要用到更底层的语言来提高运行效率

● 音视频开发涉及到的音视频编解码需要更快的处理速度,这就需要用到JNI

● 为了整合一些以前的非Java语言开发的系统

● 需要用到早期实现的C/C++语言开发的一些功能或者系统,将这些功能整合到当前的系统或者新的版本中

JNI是完善Java的一个重要功能,它让Java更加全面、封装了各个平台的差异性

jni异常处理

在android ndk开发过程中,调用java对象方法可能会抛异常,如果在ndk层中不做任何处理,那么程序就会直接崩溃;例如我们要在jni层获取apk的签名,代码如下:

     **jclass native_class = env->GetObjectClass(const_cast<jobject>(contextObject));
  jmethodID pm_id = env->GetMethodID(native_class, "getPackageManager", "()Landroid/content/pm/PackageManager;");
  jobject pm_obj = env->CallObjectMethod(const_cast<jobject>(contextObject), pm_id);
  jclass pm_clazz = env->GetObjectClass(pm_obj);
  jmethodID package_info_id = env->GetMethodID(pm_clazz, "getPackageInfo","(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");

  // 获得应用包的信息
  jobject pi_obj = NULL;

  //GET_SIGNATURES 64
  pi_obj = env->CallObjectMethod(pm_obj, package_info_id, package_name, 64);

    ...**

上述代码如果package_name查找不到就会报如下异常

捕获异常

释放资源

Native 提供了 ExceptionOccurredExceptionCheck 方法来检测是否有异常发生,前者返回的是 jthrowable 类型,后者返回的是 jboolean 类型;如果有异常,会通过 ExceptionDescribe 方法来打印异常信息,方便我们在 LogCat 中看到对应的信息

ExceptionClear 方法则是关键的不会让应用直接崩溃的方法,类似于 Java 的 catch 捕获异常处理,它会消除这次异常;这样就把由 Native 调用 Java 时的一个异常进行了处理,当处理完异常之后,别忘了释放对应的资源

不过,我们这样仅仅是消除了这次异常,还应该让调用者有异常的发生,那么就需要通过 Native 来抛出一个异常告诉 Java 调用者了

void throwByName(JNIEnv *env, const char *name, const char *msg) {
    jclass cls = env->FindClass(name);
    if (cls != NULL) {
        env->ThrowNew(cls, msg);
    }
    env->DeleteLocalRef(cls);
}
// 调用抛出异常
extern "C"
JNIEXPORT void JNICALL
Java_com_glumes_cppso_jnioperations_ExceptionOps_nativeThrowException(JNIEnv *env, jobject instance) {
    throwByName(env, "java/lang/IllegalArgumentException", "native throw exception");
}

jni错误日志分析

ndk发生崩溃的时候会在/data/tombstones文件夹下生成一个墓碑文件,形如tombstone_xx。谷歌也提供了addr2line/ objdump/ndk-stack工具

一个典型的墓碑文件内容如下

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'Android-x86/android_x86/x86:5.1.1/LMY48W/woshijpf04211939:eng/test-keys'
Revision: '0'
ABI: 'x86'
pid: 1019, tid: 1019, name: surfaceflinger  >>> /system/bin/surfaceflinger <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x4
    eax a6265c06  ebx b7467d88  ecx b7631a22  edx a6265c06
    esi 00000000  edi b6867140
    xcs 00000073  xds 0000007b  xes 0000007b  xfs 00000000  xss 0000007b
    eip b745a639  ebp bfcfc1e8  esp bfcfc150  flags 00010282

backtrace:
    #00 pc 00006639  /system/lib/libui.so (android::Fence::waitForever(char const*)+41)
    #01 pc 00034b86  /system/lib/libsurfaceflinger.so
    #02 pc 0003229e  /system/lib/libsurfaceflinger.so
    #03 pc 0002cb9c  /system/lib/libgui.so (android::BufferQueue::ProxyConsumerListener::onFrameAvailable(android::BufferItem const&)+652)
    #04 pc 000342f4  /system/lib/libgui.so (android::BufferQueueProducer::queueBuffer(int, android::IGraphicBufferProducer::QueueBufferInput const&, android::IGraphicBufferProducer::QueueBufferOutput*)+2580)
    #05 pc 0004eafb  /system/lib/libgui.so (android::Surface::queueBuffer(ANativeWindowBuffer*, int)+411)
    #06 pc 0004ce06  /system/lib/libgui.so (android::Surface::hook_queueBuffer(ANativeWindow*, ANativeWindowBuffer*, int)+38)
    #07 pc 00014bc6  /system/lib/egl/libGLES_android.so
    #08 pc 00017f73  /system/lib/egl/libGLES_android.so (eglSwapBuffers+163)
    #09 pc 00015fdb  /system/lib/libEGL.so (eglSwapBuffers+203)
    #10 pc 000013ea  /system/lib/hw/hwcomposer.x86.so
    #11 pc 00034730  /system/lib/libsurfaceflinger.so
    #12 pc 000256d4  /system/lib/libsurfaceflinger.so
    #13 pc 00024bf4  /system/lib/libsurfaceflinger.so
    #14 pc 000236fb  /system/lib/libsurfaceflinger.so
    #15 pc 0002338a  /system/lib/libsurfaceflinger.so
    #16 pc 0001e0ff  /system/lib/libsurfaceflinger.so
    #17 pc 0001d9ce  /system/lib/libutils.so (android::Looper::pollInner(int)+926)
    #18 pc 0001db73  /system/lib/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+67)
    #19 pc 0001e561  /system/lib/libsurfaceflinger.so
    #20 pc 00022ce7  /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::run()+39)
    #21 pc 00000ca3  /system/bin/surfaceflinger
    #22 pc 0001365a  /system/lib/libc.so (__libc_init+106)
    #23 pc 00000da8  /system/bin/surfaceflinger

stack:
         bfcfc110  00000000  
         bfcfc114  b6839270  
         bfcfc118  00000000  
         bfcfc11c  00000000  
         bfcfc120  b68394e0  
         bfcfc124  00000002  
         bfcfc128  00000002  
         bfcfc12c  b75d8185  /system/lib/libutils.so (android::RefBase::incStrong(void const*) const+53)
         bfcfc130  b6839270  
         bfcfc134  bfcfc1e8  [stack]
         bfcfc138  00000002  
         bfcfc13c  a6265c06  
         bfcfc140  b7467d88  /system/lib/libui.so
         bfcfc144  00000000  
         bfcfc148  b6867140  
         bfcfc14c  b745a639  /system/lib/libui.so (android::Fence::waitForever(char const*)+41)
    #00  bfcfc150  b683af18  
         bfcfc154  bfcfc1e8  [stack]
         bfcfc158  00000000  
         bfcfc15c  00000000  
         bfcfc160  00000000  
         bfcfc164  b683af18  
         bfcfc168  b75ec9c4  /system/lib/libutils.so
         bfcfc16c  b75d8285  /system/lib/libutils.so (android::RefBase::weakref_type::decWeak(void const*)+37)
         bfcfc170  00000000  
         bfcfc174  00000000  
         bfcfc178  00000000  
         bfcfc17c  00000000  
         bfcfc180  b7642968  /system/lib/libsurfaceflinger.so
         bfcfc184  bfcfc1e8  [stack]
         bfcfc188  b6867140  
         bfcfc18c  b7622b87  /system/lib/libsurfaceflinger.so

文件结构

定位工具

  • addr2line
qinqundeMacBook-Pro% ./aarch64-linux-android-addr2line -C -f -e ~/Documents/DProtect2/mylibrary/build/intermediates/cmake/debug/obj/arm64-v8a/libnative-lib.so 0000000000157efc
Java_com_dofun_dprotect_lib_LocalWork_start
/Users/qinqun/Documents/DProtect2/mylibrary/src/main/cpp/native-lib.cpp:147
  • ndk-stack

有需要完整代码的同学 可以 私信 发送 “底层源码” 即可 免费获取

现在发送还可以获得更多《Android 学习笔记+源码解析+面试视频》

JNI 特点:

二进制兼容

● 本地方法库与同一平台上所有Java 虚拟机之间实现二进制兼容,即对于给定平台开发人员只需要维护一种版本的本地方法库

效率高

为了实现实时系统,JNI 在效率与虚拟机无关性之间进行了优化,以保障高效运行

功能强

● JNI 提供了大量的函数及接口让本地方法与Java 虚拟机内核相互操作,增强两者的功能

本地代码与 Java 虚拟机之间是通过 JNI 函数实现相互操作的;JNI 函数通过接口指针来获得,本地方法将 JNI 接口指针当作参数来接受;虚拟机保证在从相同的 Java 线程中对本地方法进行多次调用时,传递给本地方法的接口指针是相同的,本地方法被不同的 Java 线程调用时,它接受不同的 JNI接口指针

尾述

有需要完整代码的同学 可以 私信 发送 “底层源码” 即可 免费获取

现在发送还可以获得更多《Android 学习笔记+源码解析+面试视频》

技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面

Android 架构师之路还很漫长,与君共勉

PS:有问题欢迎指正,可以在评论区留下你的建议和感受;

欢迎大家点赞评论,觉得内容可以的话,可以转发分享一下

相关推荐

电脑装系统用GHOST好,还是原装版本好?老司机都是这么装的

Hello大家好,我是兼容机之家的咖啡。安装Windows系统是原版ISO好还是ghost好呢?针对这个的问题,我们先来科普一下什么是ghost系统,和原版ISO镜像两者之间有哪些优缺点。如果是很了解...

苹果 iOS 14.5.1/iPadOS 14.5.1 正式版发布

IT之家5月4日消息今日凌晨,苹果发布了iOS14.5.1与iPadOS14.5.1正式版更新。这一更新距iOS14.5正式版发布过去了一周时间。IT之家了解到,苹果表示,...

iOS 13.1.3 正式版发布 包含错误修复和改进

苹果今天发布了iOS13.1.3和iPadOS13.1.3,这是iOS13发布之后第四个升级补丁。iOS13.1.2两周前发布。iOS13.1.3主要包括针对iPad和...

还不理解 Error 和 Exception 吗,看这篇就够了

在Java中的基本理念是结构不佳的代码不能运行,发现错误的理想时期是在编译期间,因为你不用运行程序,只是凭借着对Java基本理念的理解就能发现问题。但是编译期并不能找出所有的问题,有一些N...

Linux 开发人员发现了导致 MacBook“无法启动”的 macOS 错误

“多个严重”错误影响配备ProMotion显示屏的MacBookPro。...

启动系统时无法正常启动提示\windows\system32\winload.efi

启动系统时无法正常启动提示\windows\system32\winload.efi。该怎么解决?  最近有用户遇到了开机遇到的问题,是Windows未能启动。原因可能是最近更改了硬件或软件。虽然提...

离线部署之两种构建Ragflow镜像的方式,dify同理

在实际项目交付过程中,经常遇到要离线部署的问题,生产服务器无法连接外网,这时就需要先构建好ragflow镜像,然后再拷到U盘或刻盘,下面介绍两种构建ragflow镜像的方式。性能测试(网络情况好的情况...

Go语言 error 类型详解(go语言 异常)

Go语言的error类型是用于处理程序运行中错误情况的核心机制。它通过显式的返回值(而非异常抛出)来管理错误,强调代码的可控性和清晰性。以下是详细说明及示例:一、error类型的基本概念内置接口...

Mac上“闪烁的问号”错误提示如何修复?

现在Mac电脑的用户越来越多,Mac电脑在使用过程中也会出现系统故障。当苹果电脑无法找到系统软件时,Mac会给出一个“闪烁的问号”的标志。很多用户受到过闪烁问号这一常见的错误提示的影响,如何解决这个问...

python散装笔记——177 sys 模块(python sys模块详解)

sys模块提供了访问程序运行时环境的函数和值,例如命令行参数...

30天自制操作系统:第一天(30天自制操作系统电子书)

因为咱们的目的是为了研究操作系统的组成,所以直接从系统启动的第二阶段的主引导记录开始。前提是将编译工具放在该文件目录的同级目录下,该工具为日本人川合秀实自制的编译程序,优化过的nasm编译工具。...

五大原因建议您现在不要升级iOS 13或iPadOS

今天苹果放出了iPadOS和iOS13的公测版本,任何对新版功能感兴趣的用户都可以下载安装参与测试。除非你想要率先体验Dark模式,以及使用AppleID来登陆Facebook等服务,那么外媒CN...

Python安装包总报错?这篇解决指南让你告别pip烦恼!

在Python开发中,...

苹果提供了在M1 Mac上修复macOS重装错误的方案

#AppleM1芯片#在苹果新的M1Mac推出后不久,我们看到有报道称,在这些机器上恢复和重新安装macOS,可能会导致安装错误,使你的Mac无法使用。具体来说,错误信息如下:"An...

黑苹果卡代码篇三:常见卡代码问题,满满的干货

前言...

取消回复欢迎 发表评论: