您的位置:首页 > 运维架构

OPhone平台的JNI机制探索

2010-10-14 16:15 169 查看

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

view plain
copy to clipboard
print
?

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());

}

}

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++程序调用来生成所需的库文件。 如下(此文件不能被修改)

view plain
copy to clipboard
print
?

/* 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

/* 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程序中存在的参数。

view plain
copy to clipboard
print
?

#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;

}

#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虚拟机的代码.

view plain
copy to clipboard
print
?

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);

}

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层方法。如:

view plain
copy to clipboard
print
?

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: