Android系统由Java语言写很差吗?为什么按语言来要这么划分?(android是java写的吗)

jni概述

JNI(java Native Interface,Java本地接口)是一种编程框架使得Java虚拟机中的Java程序可以调用本地应用/或库,也可以被其他程序调用; 本地程序一般是用其它语言C,C 或汇编语言编写的, 并且被编译为基于本机硬件和操作系统的程序;在Android平台,为了更方便开发者的使用和增强其功能性,Android提供了NDK来更方便开发者的开发

Android系统由Java语言写很差吗?为什么按语言来要这么划分?(android是java写的吗)

Android系统按语言来划分的话由两个世界组成,分别是Java世界和Native世界; 那为何要这么划分呢?Android系统由Java写很差吗?除了性能的以外,最主要的缘由就是在Java诞生以前,就有不少程序和库都是由Native语言写的,所以, 重复利用这些Native语言编写的库是十分必要的,何况Native语言编写的库具备更好的性能

这样就产生了一个问题, Java世界的代码要怎么使用Native世界的代码呢?这就须要一个桥梁来将它们链接在一块儿,而JNI就是这个桥梁

Android系统由Java语言写很差吗?为什么按语言来要这么划分?(android是java写的吗)

经过JNI,Java世界的代码就能够访问Native世界的代码,一样的,Native世界的代码也能够访问Java世界的代码

JNI,全名 Java Native Interface,是Java本地接口; JNI是Java调用Native 语言的一种特性,通过JNI可以使得Java与C/C 机型交互;简单点说就是JNI是Java中调用C/C 的统称

JNI:是java与其他语言通信的桥梁

需要用到JNI技术的地方:

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

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

● 为了节省程序的运行时间。必须采用其他语言(比如:c/c )来提升运行效率

JNI在android中的运用场景:

● 热修复

● 插件化

● 逆向开发

● 系统源码调用等

为什么要有JNI?

Android系统由Java语言写很差吗?为什么按语言来要这么划分?(android是java写的吗)

JNI允许程序员用其他编程语言来解决用纯粹的Java代码不好处理的情况; 例如:Java标准库不支持的平台相关功能或者程序库;也用于改造已存在的用其它语言写的程序, 供Java程序调用。许多基于JNI的标准库提供了很多功能给程序员使用, 例如文件I/O、音频相关的功能

当然,也有各种高性能的程序,以及平台相关的API实现, 允许所有Java应用程序安全并且平台独立地使用这些功能; Java层可以用来负责UI功能实现,而C 负责进行计算操作

JNI框架允许Native方法调用Java对象; 就像Java程序访问Native对象一样方便;Native方法可以创建Java对象,读取这些对象, 并调用Java对象执行某些方法。当然Native方法也可以读取由Java程序自身创建的对象,并调用这些对象的方法

通常在以下几种情况下考虑使用JNI:

对处理速度有要求

● Java代码运行速度要比本地代码(C/C )运行速度慢一些,假设对程序的运行速度有较高的要求

● 能够考虑使用C/C 编写代码,然后在通过Java代码调用基于C/C 编写的部分

硬件控制

● 如前面所述:Java运行在虚拟机中,和真实运行的物理硬件之间是相互隔离的,通常我们使用本地代码C实现对硬件驱动的控制,然后再通过Java代码调用本地硬件控制代码

复用本地代码

● 假设程序的处理逻辑已经由本地代码实现并封装成了库,就没有必要再又一次使用Java代码实现一次;直接复用该本地代码,即提高了编程效率,又确保了程序的安全性和健壮性

局部引用与全局引用

Java代码与本地代码里在进行參数传递与返回值复制的时候,要注意数据类型的匹配; 对于int, char等基本类型直接进行拷贝就可以,对于Java中的对象类型,通过传递引用实现

JVM保证全部的Java对象正确的传递给了本地代码; 而且维持这些引用,因此这些对象不会被Java的gc(垃圾收集器)回收

因此,本地代码必须有一种方式来通知JVM本地代码不再使用这些Java对象;让gc来回收这些对象

JNI将传递给本地代码的对象分为两种:局部引用和全局引用

局部引用:

● 仅仅在上层Java调用本地代码的函数内有效,当本地方法返回时,局部引用自己主动回收

全局引用:

● 仅仅有显示通知VM时:全局引用才会被回收,否则一直有效,Java的gc不会释放该引用的对象

局部引用

默认的话,传递给本地代码的引用是局部引用

全部的JNI函数的返回值都是局部引用

<pre mdtype="fences" cid="n72" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">jstring MyNewString(JNIEnv *env, jchar *chars,jint len){staticjclassstringClass = NULL; //static 不能保存一个局部引用jmethodIDcid;jcharArrayelemArr;jstringresult;if(stringClass== NULL) {stringClass =env->FindClass("java/lang/String"); // 局部引用if(stringClass == NULL) {return NULL; /* exception thrown */ } } /* 本地代码中创建的字符串为局部引用,当函数返回后字符串有可能被gc回收 */ cid =env->GetMethodID(stringClass,"<init>","([C)V"); result=env->NewStringUTF(stringClass, cid, “Hello World”); returnresult;}</pre>

尽管局部引用会在本地代码运行之后自己主动释放,可是有下列情况时;要手动释放:

本地代码訪问一个非常大的Java对象时,在使用完该对象后; 本地代码要去运行比較复杂耗时的运算时,由于本地代码还没有返回。Java收集器无法释放该本地引用的对象,这时,应该手动释放掉该引用对象

本地代码创建了大量局部引用; 这可能会导致JNI局部引用表溢出,此时有必要及时地删除那些不再被使用的局部引用

比方:在本地代码里创建一个非常大的对象数组

jni.h头文件里定义了JNI本地方法与Java方法映射关系结构体JNINativeMethod

● 创建的工具函数,它会被未知的代码调用,在工具函数里使用完的引用要及时释放

● 不返回的本地函数

比如,一个可能进入无限事件分发的循环中的方法

此时在循环中释放局部引用,是至关重要的,这样才干不会无限期地累积;进而导致内存泄露

局部引用仅仅在创建它们的线程里有效;本地代码不能将局部引用在多线程间传递。一个线程想要调用还有一个线程创建的局部引用是不被同意的

将一个局部引用保存到全局变量中,然后在其他线程中使用它,这是一种错误的编程

全局引用

在一个本地方法被多次调用时,能够使用一个全局引用跨越它们

一个全局引用能够跨越多个线程,而且在被程序猿手动释放之前,一直有效; 和局部引用一样,全局引用保证了所引用的对象不会被垃圾回收

JNI同意程序猿通过局部引用来创建全局引用, 全局引用仅仅能由NewGlobalRef函数创建

以下是一个使用全局引用样例:

<pre mdtype="fences" cid="n94" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">jstringMyNewString(JNIEnv *env, jchar *chars,jint len){ staticjclassstringClass = NULL; ...省略部分代码 if(stringClass == NULL) { jclasslocalRefCls =env->FindClass("java/lang/String"); if(localRefCls == NULL) { return NULL; } /*创建全局引用并指向局部引用 */ stringClass = env->NewGlobalRef(localRefCls); /*删除局部引用 */ env->DeleteLocalRef(localRefCls); /*推断全局引用是否创建成功 */ if(stringClass == NULL) { return NULL; /* out of memory exception thrown */ } }}</pre>

在native代码不再须要訪问一个全局引用的时候,应该调用DeleteGlobalRef来释放它。假设调用这个函数失败。Java VM将不会回收相应的对象

Native方法注册

静态注册

新建 xx.java 也就是 JNI 对应的 Java层的类其中定义好要使用的方法 在xx.java目录下执行下面两条命令

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n115" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">javac com/example/xx.javajavah com.example.xx</pre>

第二条命令会在当前目录下生成 com_example_xx.h 文件,其中会生成对应的C 层方法,命名格式为 包名 类名 方法名的格式,用"_"分割 静态注册就是根据方法名,将Java方法和JNI函数建立关联,但它有以下缺点: 1、JNI层的函数名称过长; 2、声明Native方法类需要用javah生成头文件 3、初次调用Native方法时需要建立关联,影像效率 (2) 动态注册

JNI中有一种结构用来记录 Java 的 Native 方法和JNI 方法的关联关系,它就是 JNINativeMethod,它在 jni.h 中被定义

<pre mdtype="fences" cid="n108" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">typedef struct{ const char* name;//Java方法的名字 const char* signature;//Java方法的签名信息 void* fnPtr; //JNI中对应的方法指针}JNINativeMethod;</pre>

系统的 MediaRecorder采用的就是动态注册,我们来看它的JNI层是怎么做的

定义 JNINativeMethod类型的数组 gMethods,里面存储的就是 MediaRecorder 的 Native 方法与 JNI 层函数的对应关系

<pre mdtype="fences" cid="n122" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">//frameworks/base/media/jni/android_media_MediaRecorder.cppstatic const JNINativeMethod gMethods[] = { {"start", "()V", (Void*)android_media_MediaRecord_Start}, ...}</pre>

其中start 是 java 层的Native方法,对应的JNI 层的函数为 android_media_MediaRecord_start; ()V是start方法的签名信息; 定义完该数组后还需注册它,注册的函数为 register_android_media_MediaRecorder

JNI_OnLoad 函数会在调用 System.LoadLibrary 函数后调用; 因此,注册函数就被统一定义在android_media_MediaPlayer.cpp 的 JNI_OnLoad函数中

在register_android_media_MediaRecorder 方法中返回了 AndroidRuntime 的 registerNativeMethods 函数,也就是会调用该方法

在Android Runtime的registerNativeMethods函数中又返回了jniRegisterNativeMethods函数,它被定义在 JNI 帮助类 JNIHelp.cpp 中

jniRegisterNativeMethods 方法中最终通过调用的 JNIEnv 的 RegisterNatives 函数来完成JNI的注册数据类型的转换、基本数据类型的转换; 基本数据类型转换除了最后一行的 void,其他的数据类型只需要在前面加上 “j” 就可以了,第三列的 Signature 代表签名格式

Java

Native

Signature

byte

jbyte

B

char

jchar

C

boolean

jboolean

Z

void

void

V

引用数据类型的转换数组的 JNI 层数据类型需要以 “Array” 结尾,签名格式的开头都会有 “I”; 需要注意有些数据类型的签名以 ";” 结尾, 引用数据类型还具有继承关系 jclass、jstring、jarray和jthrowable都继承 jobject,而 jobjectArray、jintArray和jlongArray等类型都继承 jarray

尾述

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

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

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

(0)
上一篇 2023年5月13日 上午10:46
下一篇 2023年5月13日 上午11:02

相关推荐

  • 琅琊区科级干部任前公示(琅琊区科级干部任前公示名单)

    为在干部选拔任用工作中进一步扩大民主,广泛听取群众意见,把干部选好、选准,根据《党政领导干部选拔任用工作条例》和琅琊区委关于科级干部任前公示的有关规定,现将拟提拔使用的干部有关情况…

    科研百科 2023年1月23日
    2.0K
  • 党建引领 合规践行(党建引领 合规践行心得体会)

    近日,湖南祁阳农商银行举办“党建引领,合规践行”合规知识抢答赛,旨在“以赛促学、以赛促进、以赛促用”,激发全行员工对合规知识的热情和重视,推动形成知规守规的良好氛围。 本次合规知识…

    科研百科 2024年6月25日
    79
  • 科研平台管理办法制定说明(科研平台管理办法)

    科研平台管理办法科研平台管理办法当前,移动互联网时代,互联网已经成为一个互联网+的公共平台,它将进一步为行业的发展和社会交流提供平台,也成为公共应用领域的重要组成部分。它将成为一个…

    科研百科 2024年7月31日
    49
  • 工会经费,这些事项您都了解吗?(工会经费,这些事项您都了解吗)

    导读:提及“工会经费”,很多企业并不陌生。有关工会经费如何正确使用?我们都需要注意哪些事项?简单帮大家梳理总结一下 1.工会经费是什么? 工会经费是根据企业会计制度企业可以根据工资…

    科研百科 2023年10月5日
    189
  • 满洲里市“三会一课”实行痕迹管理

    满洲里市切实推进“三会一课”全程纪实,综合运用网络和书面两种媒介实行痕迹管理,确保党支部组织生活经常、认真、严肃,推动全面从严治党向基层延伸。 网络痕迹管理切中时代脉搏。满洲里市有…

    科研百科 2023年10月16日
    180
  • 项目管理费率

    项目管理费率是指项目管理过程中的费用,通常用于衡量项目管理的效率和质量。随着信息技术的发展,项目管理费率已经成为了一个非常热门的话题。本文将探讨项目管理费率的重要性,以及如何计算和…

    科研百科 2024年7月24日
    40
  • 工程项目管理体会

    工程项目管理体会 工程项目管理是一项复杂的任务,需要项目经理、工程师、设计师、采购人员、技术人员和其他相关方之间的紧密合作和协调。在我参与的项目中,我深刻地体会到了工程项目管理的重…

    科研百科 2024年8月18日
    49
  • 管理进度系统软件

    管理进度系统软件: 一款让组织更高效的工具 管理进度系统软件是一款能够帮助组织高效管理进度的工具。它可以帮助您跟踪项目的进展情况,协调各个团队的工作,以及识别和解决项目中出现的问题…

    科研百科 2024年8月21日
    33
  • 能娱乐能办公!酷比魔方酷玩Pad Pro,12.1寸高分屏平板949元(酷比魔方平板怎么样好用吗)_1

    酷玩Pad Pro简介 产品定位 在平板电脑市场,酷比魔方一直致力于为用户提供高性能、高品质的产品体验。酷玩Pad Pro的推出,正是公司在这一理念指导下的最新力作。它定位于一款集…

    科研百科 2024年6月14日
    83
  • 系统信息项目管理师

    系统信息项目管理师 系统信息项目管理师是负责项目开发、实施和维护的专业人员。他们负责确保项目按时、按质量完成,并且符合客户和公司的期望。在这个数字时代,系统信息项目管理师的工作变得…

    科研百科 2024年12月12日
    1