1.jni的基本工作原理  
       (1)java的本质

  想搞明白jni的本质,还要从java的本质说起.从本质上来说,java这门语言就是一门脚本语言(这是偶的个人理解,希望java大侠们不要用板砖拍我),它的运行完全依赖于脚本引擎对java的代码进行解释和执行(当然了,现代的java已经先进许多,可以从源代码编译成.class之类的中间格式的二进制文件,这种处理会大大地加快java脚本的运行速度,但是基本的执行方式仍然不变,由脚本引擎(我们称之为JVM)来执行,与python、perl之类的纯脚本相比,它只是把脚本变成了二进制格式而已.另外就是java本身对面向对象的概念支持得很好,拥有完善的功能库可供调用,把这个脚本引擎移植到所有平台上,那么这个脚本自然就实现所谓的“跨平台”了).绝大多数的脚本引擎都支持一个很显著的特性,就是可以通过c/c++编写模块,在脚本中调用这些模块,以此来类比java,也是一样的,java一定要提供一种在脚本中调用c/c++编写的模块的机制,才能称得上是一个相对完善的脚本引擎.

  (2)android中的java

  android平台从本质上是 由arm-linux操作系统 和一个叫做dalvik的java虚拟机组成的.所有在android模拟器上面看到的那些华丽的界面,都是用java语言编写的(参见android平台源代码的frameworks/base目录).目前看来dalvik只是提供了一个标准的支持jni调用的java虚拟机环境.android平台中所有的硬件相关的操作均是采用jni技术进行了封装,由java去调用jni模块,而jni模块使用c/c++调用android本身的arm-linux底层驱动.

  例如,frameworks/base/libs/ui目录下面有一个叫做“EGLDisplaySurface.cpp”的文件,里面的:

  status_t EGLDisplaySurface::mapFrameBuffer()函数中,就有直接对android的arm-linux中的framebuffer的初始化代码.

  这也更加印证了,android其实是依靠java+jni建立起来的王国.hoho,如此一来,就凸显出jni在Android开发中的重要性(当然,一些简单的小程序是完全可以只用java就搞定的).

  “jni”的子目录,这个目录将用来存放.c的文件.

  (3)编写jni模块的java调用类

  这是必然的了,jni嘛,一定要有调用者才能够工作在src的最内层目录里面添加一个叫做JniModule.java的原文件,看上去如下所示:

java代码:
  1. public class JniModule {

  2. static {
  3. System.loadLibrary("aaaa") ; 
  4. }
  5. public native static int jni_add(int a, int b) ; 
  6. }
复制代码

       注意,偶们最终会生成一个叫做libaaaa.so的arm兼容的二进制动态库,但是在使用System.loadLibrary动态载入的时候,只需要填写lib和.so之间的名字aaaa即可,在此实验的功能仅仅是两个数字a和b的求和计算以及如何在jni的c语言模块中把log日志打印到logcat中.

        在JniTest.java中,偶们可以如下调用这个类:

java代码:
  1. public void onClick(View v) {

  2. String ss ; 
  3. int a = 3 ; 
  4. int b = 4 ; 

  5. ss = "" ; 
  6. switch(v.getId()) {
  7. case R.id.button1:
  8. ss = "a="+String.valueOf(a)+","+"b=" + String.valueOf(b) + "," + "a+b=" + 
  9. String.valueOf(JniModule.jni_add(a, b)); 

  10. setTitle(ss) ;

  11. break ; 
  12. case R.id.button2:
  13. setTitle("button2 click") ; 
  14. break ; 
  15. case R.id.button3:
  16. int pid = android.os.Process.myPid(); 
  17. android.os.Process.killProcess(pid);
  18. break ; 


  19. }

复制代码

       注意,这里的button3是很重要的,功能是得到当前程序的进程id,然后显示地杀掉它!
       为什么要这么做呢?原因在于,android里面的常规退出函数并没有真正地关闭当前运行的进程,而是切换到后台去了。这对普通的java应用看上去很平常,而且可以加速再次启动该程序的速度,但是对于带有jni模块的java程序而言就是恶梦,因为程序没有真的关闭。所以那个libaaaa.so库,会一直停留在内存中,这时候如果你希望把旧的so库替换成新的库,那就要重启手机才行。。。很痛苦,所以想到了这种办法,直接杀掉自己,那么下一次启动的时候就会自动重新载入最新的so库。
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐