OPhone平台的JNI机制探索

OPhone平台的JNI机制探索

 

      JNI是Java Native Interface的缩写,中文可译为Java本地调用。Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互.

      OPhone下的JNI使得Dalvik虚拟机内部运行的Java代码能够与其他语言编写的应用程序或者库(例如C++编译完成之后的 so文件)进行交互.虽然JNI并不是Android的技术发展方向,但是作为一种重要的技术手段可以解决Linux内核和Dalvik虚拟机之间的交互 问题.

      JNI提供的交互通道是双向的,即Java代码可以调用C/C++库中的功能, 另外,C/C++库也可以调用到Dalvik虚拟机之上的Java库功能.下面就针对这两种应用方式做详细说明。

1:通过JNI实现Java调用C/C++库功能
      在Android的代码中,这样的例子不胜枚举.比如Media中的视频音频播放控制,Web页面的解析渲染显示,Graphics中 Paint功能等等,这些Java类在C++部分都有负责实现功能的C++类库相对应.较之Java,C++有更强的平台操控性和执行效率,所以比如像 WebKit这样需要复杂运算和平台依赖的核心类库来说,大部分的代码都是由C++来实现的.Brower这种强烈依赖WebKit的应用程序在 OPhone下都是用Java来实现的,这样JNI就负责了Java层与webcore.so(WebKit编译之后的类库)之间的通信.

     打开Eclipse,我们做一个HelloJNI的例子.
     创建一个Android工程,命名为HelloJNI
     编写Java端文件: HelloJNI.java

  1.   public   class  HelloJNI  extends  Activity {  
  2.     static  {   
  3.   
  4.           String libPath="cpluslib.so" ;  
  5.           //向Dalvik加载指定相对路径的C++库。   
  6.           System.loadLibrary(libPath);  
  7.          }  
  8.   
  9.    //需要JNI的函数必须声明为 public native static   
  10.    public   native   static   int  get();  
  11.    public   native   static   void  set( int  i);  
  12.   
  13.     @Override   
  14.     public   void  onCreate(Bundle savedInstanceState) {  
  15.         super .onCreate(savedInstanceState);  
  16.         setContentView(R.layout.main);  
  17.         set(100 );  
  18.         Log.v("HelloJNI" , "value=" +get());         
  19.     }  
  20. }   
  21.     
  22.    
 public class HelloJNI extends Activity {
    static { 

          String libPath="cpluslib.so";
          //向Dalvik加载指定相对路径的C++库。
          System.loadLibrary(libPath);
         }

   //需要JNI的函数必须声明为 public native static
   public native static int get();
   public native static void set(int i);

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        set(100);
        Log.v("HelloJNI","value="+get());       
    }
} 
  
 

       用javac HelloJNI.java编译它,会生成HelloJNI.class。
再用javah HelloJNI ,则会在当前目录下生成HelloJNI.h文件,这个文件需要被C/C++程序调用来生成所需的库文件。 如下(此文件不能被修改)

  1. /* DO NOT EDIT THIS FILE - it is machine generated */    
  2. #include   
  3. /* Header for class HelloJNI*/    
  4. #ifndef _Included_HelloJNI  
  5. #define _Included_HelloJNI  
  6. #ifdef __cplusplus   
  7. extern "C"  {   
  8. #endif   
  9. /*   
  10. * Class: HelloJNI  
  11. * Method: get   
  12. * Signature: ()I   
  13. */    
  14. JNIEXPORT jint JNICALL Java_HelloJNI_get (JNIEnv *, jclass);   
  15. /*   
  16. * Class: HelloJNI  
  17. * Method: set   
  18. * Signature: (I)V   
  19. */    
  20. JNIEXPORT void  JNICALL Java_HelloJNI_set (JNIEnv *, jclass, jint);   
  21. #ifdef __cplusplus   
  22. }   
  23. #endif   
  24. #endif  
/* DO NOT EDIT THIS FILE - it is machine generated */ 
#include 
/* Header for class HelloJNI*/ 
#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus 
extern "C" { 
#endif 
/* 
* Class: HelloJNI
* Method: get 
* Signature: ()I 
*/ 
JNIEXPORT jint JNICALL Java_HelloJNI_get (JNIEnv *, jclass); 
/* 
* Class: HelloJNI
* Method: set 
* Signature: (I)V 
*/ 
JNIEXPORT void JNICALL Java_HelloJNI_set (JNIEnv *, jclass, jint); 
#ifdef __cplusplus 
} 
#endif 
#endif


        打开Vim,编写C++端代码
        在具体实现的时候,我们只关心两个函数原型
        JNIEXPORT jint JNICALL Java_HelloJNI_get (JNIEnv *, jclass); 和
        JNIEXPORT void JNICALL Java_HelloJNI_set (JNIEnv *, jclass, jint);
        这里JNIEXPORT和JNICALL都是JNI的关键字,表示此函数是要被JNI调用的。函数的名称是JAVA_再加上java程序的package路径再加函数名组成的。参数中,我们也只需要关心在JAVA程序中存在的参数。

  1. #include  "HelloJNI.h"    
  2. int  i =  0 ;   
  3. JNIEXPORT jint JNICALL Java_HelloJNI_get (JNIEnv *, jclass)   
  4. {   
  5. return  i;   
  6. }   
  7. JNIEXPORT void  JNICALL Java_HelloJNI_set (JNIEnv *, jclass, jint j)   
  8. {   
  9. i = j;   
  10. }  
#include "HelloJNI.h" 
int i = 0; 
JNIEXPORT jint JNICALL Java_HelloJNI_get (JNIEnv *, jclass) 
{ 
return i; 
} 
JNIEXPORT void JNICALL Java_HelloJNI_set (JNIEnv *, jclass, jint j) 
{ 
i = j; 
}

       编译链接生成cpluslib.so,将HelloJIN.apk和cpluslib.so Push到手机的/System/app下,运行.就可以在Log中看到.

       2:通过JNI C/C++调用Java

          在OPhone的系统框架中,在Applications和Linux kernal中间有三部分. Application Framework, Libraries, Android Runtimes.而Application Frameworks提供了Java层的框架API和核心对象,如Activity,View,ContentProvider,Service等等.他 们都是基于Dalvik虚拟机的Java类库. 与之相对的,Libraries都是C++类库,他们为上面的App Frameworks提供了核心对象的基础API,而后者对Libraries进行了包装来提供给Apps进行调用. C++ Libraries与App Frameworks的交互基本上都是通过JNI来实现的. 


         举一个JNI C/C++调用Java的例子. WebKit加载网页的过程中, 需要调用到Java层的HttpClient做网页的下载,C/C++要调用OPhone中Java程序的功能,必须先加载Dalvik虚拟机,由 Dailvk虚拟机解释执行apk/odex/dex文件。为了初始化Dalvik虚拟机,JNI提供了一系列的接口函数,通过这些函数方便地加载虚拟机 到内存.

        1.加载虚拟机:
              下面是WebKit加载Dalvik虚拟机的代码.

  1. static  jint KJS_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)  
  2. {  
  3.     static   void * javaVMFramework =  0 ;  
  4. //通过Dlopen来动态加载Java虚拟机   
  5.     if  (!javaVMFramework)  
  6.         javaVMFramework = dlopen("/System/Library/Frameworks/JavaVM.framework/JavaVM" , RTLD_LAZY);  
  7.     if  (!javaVMFramework)  
  8.         return  JNI_ERR;  
  9.     static  jint(*functionPointer)(JavaVM**, jsize, jsize *) =  0 ;  
  10.     if  (!functionPointer)  
  11.         //调用创建虚拟机的函数,从已经加载的动态连接库中.   
  12.         functionPointer = (jint(*)(JavaVM**, jsize, jsize *))dlsym(javaVMFramework, "JNI_GetCreatedJavaVMs" );  
  13.     if  (!functionPointer)  
  14.         return  JNI_ERR;  
  15.     return  functionPointer(vmBuf, bufLen, nVMs);  
  16. }  
static jint KJS_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
{
    static void* javaVMFramework = 0;
//通过Dlopen来动态加载Java虚拟机
    if (!javaVMFramework)
        javaVMFramework = dlopen("/System/Library/Frameworks/JavaVM.framework/JavaVM", RTLD_LAZY);
    if (!javaVMFramework)
        return JNI_ERR;
    static jint(*functionPointer)(JavaVM**, jsize, jsize *) = 0;
    if (!functionPointer)
        //调用创建虚拟机的函数,从已经加载的动态连接库中.
        functionPointer = (jint(*)(JavaVM**, jsize, jsize *))dlsym(javaVMFramework, "JNI_GetCreatedJavaVMs");
    if (!functionPointer)
        return JNI_ERR;
    return functionPointer(vmBuf, bufLen, nVMs);
}

2.获取指定对象的类定义:
      已知类名的情况使用FindClass来获取,如获得Java中的HashMap
      jclass mapClass = env->FindClass("java/util/HashMap");

      通过对象直接得到类定义GetObjectClass
      jCalss clazz=env->GetObjectClass(obj);

3.获取要调用的方法:
       比如Java端有个函数 void setTitle(String title);
       C++部分要通过JNI获得这个方法:
       jMethodID mSetTitle=env->GetMethodID(clazz,"setTitle","Ljava/lang/String;)V");
第三个参数是这个方法的Signature.如果不知道,那么运行javap -s -p 类名 就可以得到了.

4.调用JAVA层的方法
       如上面已经得到SetTitle的Method ID,只需要运行env->CallVoidMethod(env, clazz , mSetTitle).
另外还有CallObjectMethod()方法,用来调用有返回值的java层方法。如:

  1. jobject obj = env->CallObjectMethod(env,calzz, dialog, userGesture);  
jobject obj = env->CallObjectMethod(env,calzz, dialog, userGesture);

      更加详尽的JNI资料请参见JNI白皮书,另外在WebKit Android版本的WebCoreFrameBridge.cpp和WebCoreJni.cpp能找到更多相关的应用实例。

http://www.ophonesdn.com/article/show/116

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值