Java Native Interface ( JN I)是Java本地接口,所谓的本地(native) —般是指C/C++ ( 以下统称C)
语言。当使用Java进行程序设计时,一般主要有三种情况需要C/C++语言的协助。
• 调用驱动。由于操作系统所提供的驱动一般都是C/C++ 接口,Java语言本身不具备操作这些驱动的
能力。
• 对于某些大量数据处理的模块,Java的效率可能远低于C/C++,因此,程序员希望使用C/C++ 去完成。
• 对于某些功能模块,可能Java和C/C++ 的效率差不多,但是这些模块已经存在已有的C/C++ 代码,程序
员不想再用Java重写,而只想重新利用已有的C/C++ 代码。
这就是Java提出JNI概念的原因。无论出于什么原因,从程序的角度来看,JNI接口主要包含两种
情况。第一种是从Java中访问C/C++,第二种是从C/C++ 中访问Java,只要解决了这两个问题,那么就可以任
意进行Java和C/C++ 的应用组合。Framework中大量使用JNI完成本地接口的实现,因此,理解JNI是阅读kernel的重中之重。

1. java 访问 c、c++

Java中可以定义某个函数为native类型,对于native函数,只需要声明即可,因为该函数的实现是
native的,即由相应的C 去实现。Java编译器遇到native函数时,不会关心该函数的具体实现,因此,
编译上不会出任何差错。
程序运行时,在调用native方法之前,程序员必须把C 所生成的动态库装载进来,否则程序会因
为找不到相应的native方法而出错。关于如何把C 程序编译为动态库,将在第18章中的Make系统中
介绍,这牵扯到包含jni.h头文件等及编译选项的设置。
当调用native函数时,Java会自动产生一个对应的C 中的函数名称,因为Java中声明的函数名称
和C 中实现的函数名称是不同的。其关系为,后者等于包名加前者的名称,并且中间以下画线分隔,
比如,Framework中AssetManager类中声明了以下方法:

AssetManager.java (base\core\java\android\content\res)

private native final String[] getArrayStringResource(int arrayRes);

android_util_AssetManager.cpp (base\core\jni)中找到JNI关系映射数组:

static JNINativeMethod gAssetManagerMethods[] = {
...
// Arrays.
{ "getArrayStringResource","(I)[Ljava/lang/String;",
(void*) android_content_AssetManager_getArrayStringResource },
{ "getArrayStringInfo","(I)[I",
(void*) android_content_AssetManager_getArrayStringInfo },
{ "getArrayIntResource","(I)[I",
(void*) android_content_AssetManager_getArrayIntResource },
...
}

同时,我们可以在android_util_AssetManager.cpp (base\core\jni)中找到void*) android_content_AssetManager_getArrayStringResource的定义。

static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz,
jint arrayResId)
{
AssetManager* am = assetManagerForJavaObject(env, clazz);
if (am == NULL) {
return NULL;
}

.......

res.unlockBag(startOfBag);
return array;
}

以上这种映射关系并不是Java编译器内含的,程序员完全可以改变,但这是一种编程规范。事实上,
当Java调用native时,编译器会向native引擎传递调用者的包名,以及函数名称,还有参数类型,仅
此而已,当然这也足矣,native引擎根据这些信息决定应该具体调用哪个本地函数。native引擎中
AndroidRuntime类提供了一个registerNativeMethods()函数,可以通过该函数来定义Java native函数和C
函数名称的映射关系。

如下:

int register_android_content_AssetManager(JNIEnv* env)
{
...;
return AndroidRuntime::registerNativeMethods(env,
"android/content/res/AssetManager", gAssetManagerMethods, NELEM(gAssetManagerMethods));
}

在产生的C/C++ 函数中,会包含至少两个参数。前者是JNIEnv对象,该对象是一个Java虚拟机(JVM)
所运行的环境,相当于JVM的“管家”,通过它可以访问JVM内部的各种对象;第二个参数jobject是
调用该函数的对象,本例中指的就是AssetManager对象,第三个参数是java函数中的参数。

在以上的转换关系中,大家可能会注意到native中所使用的类型和Java中有所不同。比如Java中
的int在native中为jint,返回值中String□变为jobjectArray,这些具体的定义实际上是在jni.h中,该文
件所在的路径为:

Jni.h (kernel\android\jb\development\ndk\platforms\android-3\include)

里面还有其他类型的定义。

同时,不同的platforms会有不同的定义,这个很好理解,因为Java的类型是跨平台的,而各自平台的
CPU数据宽度是不同的,所以必须有各自的类型定义。
以上介绍了 Java和C 函数的名称转换,那么,具体怎么操作呢?在程序设计时,如果你已经定义
好了 Java代码,如何实现相应的native C 代码呢?
你可能会想:“那就按照这种转换关系,手工编写相应的C 代码,然后编译成动态库,并在Java
代码执行时加载该库就可以了(需要使用System.loadLibrary( “ lib—name” )函数装载该库。)。

没错,是这个样子,为了辅助你这样做,Java还提供了一个javah工
具,该工具可以从一个Java文件自动生成相应的头文件,剩下的就是你根据这些头文件再实现具体的
内部代码即可。

2.c/c++ 访问 java

这种情况似乎比较少,C 为什么还要访问Java呢?这个也容易理解,如果C 中需要使用Java的某
个变量而进行相应的处理,或者C 中也想调用Java中的某个函数完成某些操作,那么C 就要访问Java。
由于Java中的函数在native引擎中并没有直接的函数指针,Java函数只能由Java引擎去执行,而
不是C。所以,C 访问Java不能通过函数指针,而只能通过通用的参数接口,正如Java调用C 一样。
Java把类名、函数名称、参数类型传递给native引擎,然后由native引擎处理C 函数,同理,C 调用
Java时,也需要把想要访问的类名、函数名称、参数传递给Java引擎。其步骤如下:

(1)获取Java对象的类。
cls = env->GetObjectClass(jobject);

其中env为Java调用C 函数时的第一个参数,这意味着C 调用Java函数只能在Java调用C 函数
中进行,否则无法获取env变量。换句话说,对于C 来讲,就是“你不惹我,我不惹你”。jobject为第
二个参数。cls的类型是jelass。

2.获取Java函数的id 值。
jmethodld mid = env->GetMethodId(cls,"method_name","([Ljava/lang/String;)V");

该方法中第二个参数为Java中的函数名称,第三个参数值得注意,它代表了 Java函数的参数和返
回值,参数在括弧之中,返回值在括弧之外。本例中,参数[Ljava/lang/String代表了 String类型的参数,
由于String本身是一个类,而不是Java的原子类型,所以前面加了包的名称,并用斜线分隔,最前面
还要用一个中括弧进行标识,后面还要用分号隔离。返回值V 代表void.

3.找到了函数后,就可以调用该函数了。
env->CallXXXMethod(jobject,mid,ret);
其中XXX代表了函数的返回值类型,具体包括Void、Object、Boolean、Byte、Char、Short、Int、
Long, Float、Double。在我看来,JNI提供的这种按类型调用并不是必需的,只是为了某种灵活,因为
该函数的第三个参数是保存返回值的变量,所以JN I内部完全可以根据ret的类型来选择把Java的执行
结果进行格式转换。第二个参数mid即为第二步中所获得的函数id。

通过以上三步,实现了 C 中调用Java函数的目标。还有一个问题,C 中如何访问Java中的变量呢?
实现步骤如下:

(1)获取Java对象的类。
cls = env->GetObjectClass(jobject);

2.获取Java变量的id 值。
jmethodld fid = env->GetfiledId(cls,"filed_name","I");

参数filed_name 为Java变量的名称,第三个参数为变量的类型,其格式与上面相同。

3.获取变量值。

value=env->GetXXXField(env,jobject,fid);
该函数的参数与上面的显著不同,其中第」、第二个参数为原装Java访问C 函数的前两个参数,
该方法以返回值的方式获取变量值,而不是通过参数引用。

android framework 之JNI的更多相关文章

  1. Android深入理解JNI(二)类型转换、方法签名和JNIEnv

    相关文章 Android深入理解JNI系列 前言 上一篇文章介绍了JNI的基本原理和注册,这一篇接着带领大家来学习JNI的数据类型转换.方法签名和JNIEnv. 1.数据类型的转换 首先给出上一篇文章 ...

  2. 北京联想招聘-Android Framework高级工程师(7-10年) 加入qq 群:220486180 或者直接在此 留言咨询

    Job ID #: 45038 Position Title: Android Framework高级工程师 Location: CHN-Beijing Functional Area: Resear ...

  3. 深入了解android平台的jni(一)

    android中很多Java类都具有native接口,这些接口由本地实现,然后注册到系统中.     主要的JNI代码放在以下的路径中:frameworks/base/core/jni/,这个路径中的 ...

  4. 【转】 Android 开发 之 JNI入门 - NDK从入门到精通

    原文网址:http://blog.csdn.net/shulianghan/article/details/18964835 NDK项目源码地址 : -- 第一个JNI示例程序下载 : GitHub  ...

  5. Android学习笔记--JNI的使用方法

    1.JNI是什么 JNI是Java Native Interface的缩写,它提供若干的API实现Java与其他语言之间的通信.而Android Framework由基于Java语言的的Java层与基 ...

  6. 解析android framework下利用app_process来调用java写的命令及示例

    解析android framework下利用app_process来调用java写的命令及示例 在android SDK的framework/base/cmds目录下了,有不少目录,这些目的最终都是b ...

  7. 【Android 应用开发】Android 开发 之 JNI入门 - NDK从入门到精通

    NDK项目源码地址 : -- 第一个JNI示例程序下载 : GitHub - https://github.com/han1202012/NDKHelloworld.git -- Java传递参数给C ...

  8. Android Framework中的线程Thread及它的threadLoop方法

    当初跟踪Camera的代码中的时候一直追到了HAL层,而在Framework中的代码看见了许很多多的Thread.它们普遍的特点就是有一个threadLoop方法.依照字面的意思应该是这个线程能够循环 ...

  9. .Net程序员玩转Android系列之二~Android Framework概要(1)

    从windows操作系统说起 人们总是喜欢从将陌生的事物和自己所了解的东西关联起来,以加深对未知事物的了解,这一讲我们从windows操作系统说起,逐步引领带大家走入android的世界.写任何程序都 ...

随机推荐

  1. CF 1047 C - Enlarge GCD [素数筛]

    传送门:http://codeforces.com/contest/1047/problem/C 题意:给出n个数字,求最少删除几个数可以使剩下的数字的GCD大于n个数字的GCD 思路:最开始想的是先 ...

  2. Opencv笔记(七)——访问与操作像素

    一.获取矩阵的元素 1.获取三维矩阵img[i,j]处的元素 (b,g,r) = image[i,j],image大小为:MxNxK. 2.获取三维矩阵的子矩阵的全部元素 newimage = ima ...

  3. MySQL_备份

    Mysql 的备份 前情了解与小试 三种级别的备份与恢复数据:​1.表级别备份:mysqldump [OPTIONS] database [tables]mysqldump -p密码 库名 表名 &g ...

  4. iOS 有关二维码

    一.把相应的URL生成二维码 利用有关图像变成的框架 #import <CoreImage/CoreImage.h> 步骤:实例化二维码滤镜---->恢复滤镜属性---->将字 ...

  5. NOIP2000提高组T1 进制转换

    https://www.luogu.org/problem/P1017 题目描述 我们可以用这样的方式来表示一个十进制数: 将每个阿拉伯数字乘以一个以该数字所处位置的值减1为指数,以10为底数的幂之和 ...

  6. 22)PHP,数组排序函数

    详情见:   手册:函数参考-->变量和类型相关扩展-->数组--->对数组进行排序

  7. ipv6 mac地址转化为linklocal地址

    mac  3c:32:66:67:dd:46 linklocal地址 前六位固定  fe80:: 第七位  mac地址第一个byte进行如下计算 (byte) ((byte) (macbyte &am ...

  8. yum pip

    方式1(yum安装):1.首先安装epel扩展源:[root@localhost ~]#  yum -y install epel-release如果没有安装epel扩展源而直接安装python-pi ...

  9. java 解析URL里的主域名及参数工具类

    java 解析URL里的协议及参数工具类,解析URL中的主域名,并统一把协议修改成http或去掉协议 public class UrlDomainUtils { private static fina ...

  10. log4j2.xml 配置

    动态生成日志, 日志路径: log4j2.xml 配置如下: <?xml version="1.0" encoding="UTF-8"?> < ...