Android应用开发很少直接使用JNI技术,但是JNI技术却在广泛使用在Android系统中,只是被google很好的封装起来了,JNI是Java Native Interface的缩写,也就是我们常说的Java本地调用。我们可以用JNI技术实现java中去调用Native语言(C/C++),也可以在Native中去调用java语言;
Java也是能实现Native语言所实现的功能,但是既然Native已经实现,我们又何必去重复制造轮子呢。JNI是一个架在Java与Native两个岛屿之间的桥梁。
我们以MediaScanner类来研究下Android中如何使用JNI技术的,在Java中我们MediaScanner在framework\base\media\java\android\media目录,这个类中的某些方法会去调用Native中的方法。在JNI层,对应的是libmedia_jni.so文件,media_jni是JNI库的名字,media是Native层库的名字,也就是libmedia库。jni表示他是一个jni库,命名规则基本上采用 “lib模块名_jni.so”的命名方式。Native层对应的是libmedia.so,具体的功能实现也是在这里。Java代码通过JNI库libmedia_jni.so和Native层libmedia.so交互。注意Java虚拟机只能识别加载动态库。
我们来看MediaScanner.java文件,主要加载JNI库,还有一个是调用native函数。基本上在任何地方都可以加载这个库,只要在调用native函数前,通行的做法是在类的static语句中加载,调用System.loadLibray方法就可以,该方法的参数是动态库的名字。
public class MediaScanner
{
static {
System.loadLibrary("media_jni");//加载对应的JNI库,media_jni是JNI库名,在加载的时候会扩展成libmedia_jni.so
native_init();//调用native_init函数
}
....
public void scanDirectories(String[] directories,String volumeName) {
....
}
....
private static native final void native_init(); //声明一个native函数。native为Java的关键字,表示会由Native层实现
....
private native void processFile(String path,String mimeType, MediaScannerClient client);
....
}
MediaScanner的JNI层代码在android_media_MediaScanner.cpp中,代码路径framewor/base/media/jni
这个函数是native_init的JNI层实现
static void android_media_MediaScanner_native_init(JNIEnv *env)
{
ALOGV("native_init");
jclass clazz = env->FindClass(kClassMediaScanner);
if (clazz == NULL) {
return;
}
fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
if (fields.context == NULL) {
return;
}
}
这是processFile在JNI中的实现
static void
android_media_MediaScanner_processFile(
JNIEnv *env, jobject thiz, jstring path,
jstring mimeType, jobject client)
{
ALOGV("processFile");
// Lock already hold by processDirectory
MediaScanner *mp = getNativeScanner_l(env, thiz);
if (mp == NULL) {
jniThrowException(env, kRunTimeException, "No scanner available");
return;
}
if (path == NULL) {
jniThrowException(env, kIllegalArgumentException, NULL);
return;
}
const char *pathStr = env->GetStringUTFChars(path, NULL);
if (pathStr == NULL) { // Out of memory
return;
}
const char *mimeTypeStr =
(mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL);
if (mimeType && mimeTypeStr == NULL) { // Out of memory
// ReleaseStringUTFChars can be called with an exception pending.
env->ReleaseStringUTFChars(path, pathStr);
return;
}
MyMediaScannerClient myClient(env, client);
MediaScanResult result = mp->processFile(pathStr, mimeTypeStr, myClient);
if (result == MEDIA_SCAN_RESULT_ERROR) {
ALOGE("An error occurred while scanning file '%s'.", pathStr);
}
env->ReleaseStringUTFChars(path, pathStr);
if (mimeType) {
env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
}
}
java层是通过文件名找到JNI层的android_media_MediaScanner_native_init,到这里Java层的native函数与JNI层对应的实现函数关联起来了。