码迷,mamicode.com
首页 > 移动开发 > 详细

深入理解Android(2)__深入理解JNI

时间:2015-10-13 19:02:19      阅读:156      评论:0      收藏:0      [点我收藏+]

标签:

前言:前面一贴,我们大概了解了Android系统的架构,对于每个层次的东东也有了基本的了解,那么我们就可以正式动工,开始逐个突破,攻克每一个难点,你有没有很激动?OK,下面我们就拿JNI来练练手。

  JNI:Java NativeInterface(Java本地调用),说的简单点,JNI就是一种技术,它是干什么的?主要有两点:

    (1)Java程序中的函数可以调用Native语言写的函数,Native一般就是指C/C++编写的函数;

    (2)Native程序中的函数可以调用Java层的函数。

  看到这个,大家怎么想?其实,我们可以把JNI当作一个桥梁,连接着Java层与Native层。

                技术分享                                            技术分享

  虽然JNI是用Native编写的,但是我们依然把它归类于一个单独的层:JNI层。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  1、JNI实例:MediaScanner(结合层次图进行分析)---多媒体系统的重要组成部分,功能是扫描媒体文件,得到诸如歌曲时长、歌曲作者等媒体信息,并将它们存入到媒体数据库中。

    (1)Java世界对应的是MediaScanner,MediaScanner这个类有一些函数是需要Native层来实现的;

    (2)JNI对应的是libmedia_jni.so,media_jni是JNI库的名称。其中_线前的media是native层库的名字,这里就是libmedia库。_线后的jni代表它是一个JNI库。JNI库的名称可以随便取,但是遵循以下命名规则:"lib模块名_jni.so";

    (3)Native对应的是libmedia.so,这个库完成了实际的功能;

    (4)Java层将通过JNI库的libmedia_jni.so与Native库的libmedia.so交互。

  我们先来看看与JNI有关的代码:

 1 public class MediaScanner
 2 {
 3        static{
 4             System.loadLibrary("media_jni");  //加载对应的JNI库
 5             native_init();  //调用native函数
 6     } 
 7     ......
 8     //非native函数
 9     public void scanDirectories(String[] directories, String volumeName)               
10     {
11     ......
12     }
13     private static native final void native_init();     //申明一个native函数,native为Java关键字,表示它将由JNI层完成。
14     ...... 
15     private native void processFile(String path, String mimeType, MediaScannerClient client); 
16     ......      
17 }

  以上的代码列出了两个比较重要的点:

  (1)加载JNI库:要调用Native,就必须通过一个位于JNI层的动态库来实现,一般是在类的static语句中加载,方式是:System.loadLibrary,加载动态库的名字后,系统会自动根据不同的平台拓展成真实的动态库文件名,例如:在Linux中会拓展成libmedia_jni.so,而在windows平台上则会拓展成media_jni.dll

  (2)Java的native函数:我们发现在native_init和processFile函数前都有native关键字,它表示这两个函数将由JNI层来实现。

  所以,你如果要想使用JNI,只要完成以上两项工作。不过JNI要完成任务,可是比较复杂的,下面我们会一起分析。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  2、JNI层MediaScanner分析

    由于时间的问题,部分JNI层代码用图片的形式表示。

         技术分享

  此时,我相信你可能会产生疑惑:如何才能知道Java层的native_init函数对应的是JNI层的android_media_MediaScanner_native_init函数呢?

  (1)注册JNI函数

  大家知道,native_init函数位于android.media包中,它的全路径名称是:android.media.MediaScanner.native_init,而JNI层函数的名字是android_media_MediaScanner_native_init。

  有没有发现问题?好了,我直接告诉你把:在Native语言中,符号 “.” 是有着特殊含义的,所以JNI层需要把Java函数名称(包括包名) 中的 “.” 换成 “ _ ”,也就是通过这种方式,native_init找到了自己JNI层的本家兄弟android_media_MediaScanner_native_init的。

  其实上面,我们讨论的就是一个“注册”的问题。“注册”就是:将Java层的native函数和JNI层对应的实现函数关联起来。有了这种关联,调用Java层的native函数事,就能顺利转到JNI层对应的函数执行了。

  “注册”有两种方式:(对于关联的具体不过代码不作分析,我们大概了解注册的原理即可,如果感兴趣的同学,可以给我回复,有必要的话,我会专门写一个帖子讲解一下)

    · 静态注册:就是根据函数名来建立Java函数和JNI函数之间的关联关系(其实就是保存了JNI层函数的函数指针),而且它要求JNI层函数的名字必须遵循特定的格式。

    · 动态注册:如果我们专门使用一个结构(JNINativeMethod)来保存这种关联关系,这样我们就可以直接让native函数知道JNI层对应函数的函数指针,这样的话,是不是提高了效率呢?答案是肯定的!

  我们直接给出答案:当Java层通过System.loadLibrary加载完JNI动态库后,紧接着会查找该库中一个叫JNI_Onload的函数(此函数在android_media_MediaPlayer.cpp中)。如果有,就调用它。而动态注册的工作就是在这里完成的。

  (2)数据类型转换问题

  在Java中调用native函数,传递的参数是Java数据类型,那么这些参数传递到JNI层会变成什么?

  我们知道,Java的数据类型分为两种:基本数据类型引用数据类型

  对于这两个类型,JNI也是区分对待的,下面我们一起来看看:

    ·  基本数据类型的转换

        技术分享

      需要注意一下字长的变化,比如jchar的字长为16位,占两个字节,这与普通的char占一个字节是不一样的

    ·  引用数据类型的转换

        技术分享

  (3)JNIEnv介绍

        技术分享

    我们来分析以下JNI层函数的5个参数:后面三个参数,大家应该都比较清楚,它们对应Java中的native函数3个参数,那么另外两个参数是什么含义呢?

    我们先来看一下jobject:代表Java层的MediaScanner对象,它表示在哪个MediaScanner对象上调用的processFile。如果Java层是static函数,那么这个参数将是jclass,表示是在调用哪个Java Class的静态函数。

    下面,我们正式引出主角:JNIEnv

    JNIEnv:是一个与线程相关的代表JNI环境的结构体。

    内部结构:

              技术分享

    JNIEnv实际上就是提供了一些JNI系统函数,通过这些函数可以做到:

    1、调用Java的函数;2、操作jobject对象等很多事情

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  在下一个帖子,我们将一起来看看如何使用JNIEnv的函数,并且会跟大家介绍一个关于JNIEnv的重要知识点。

  

深入理解Android(2)__深入理解JNI

标签:

原文地址:http://www.cnblogs.com/pepsimaxin/p/4874938.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!