chromium中的JNI

近期在学习《深入理解Android 卷I》,刚刚读了第2章:深入理解JNI,感觉对JNI理解更深了。出于学以致用的原则,下面将分析一下chromium中的JNI。

《深入理解Android》中讲到,JNI注册有两种方法:静态方法和动态方法。在通常的软件开发中,会采用动态注册。但在chromium中,却找不到JNI注册代码,C++回调Java代码也没有找到FindClass/GeMethodID之类的代码。原来,绝顶聪明的google工程师将这一过程自动化了,也就是在编译过程中根据Java中的特殊标记,自动生成JNI代码。

首先看看native方法是如何注册的吧。为了说明的更清楚,以下将以base模块的SystemMonitor.java为例。在该类定义的开始,有@JNINamespace("base::android")这样的标记,这表明,native部分的实现代码都位于该命名空间。我们知道,JNI方法应该是export出来的C函数,所以如果native部分的实现是C++代码,肯定会有一个引出函数,Java代码真正调用的该引出函数,再由引出函数调用C++实现。

在SystemMonitor类中,有这样一个native方法:

private static native void nativeOnBatteryChargingChanged();

方法名中是否含有native关系不大,不过这种命名方式更容易让人一眼就可以看出它是一个native方法。其对应的native实现代码(位于system_monitor_android.cc)为:

void OnBatteryChargingChanged(JNIEnv* env, jclass clazz) {
SystemMonitor::Get()->ProcessPowerMessage(SystemMonitor::POWER_STATE_EVENT);
}

现在就需要弄明白Java代码是如何调用到这个C++函数上了,system_monitor_android.cc倒是提供了一个RegisterSystemMonitor函数:

bool RegisterSystemMonitor(JNIEnv* env) {
return base::android::RegisterNativesImpl(env);
}

问题是,翻遍了chromium的源码,都没有找到base::android::RegisterNativesImpl的实现,后来是在out目录下的SystemMonitor_jni.h中找到其实现,在这里我们看到了《深入理解Android》所讲的JNI动态注册的代码:

static bool RegisterNativesImpl(JNIEnv* env) {

g_SystemMonitor_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
base::android::GetUnscopedClass(env, kSystemMonitorClassPath)));
static const JNINativeMethod kMethodsSystemMonitor[] = {
{ "nativeOnBatteryChargingChanged",
"("
")"
"V", reinterpret_cast<void*>(OnBatteryChargingChanged) },
{ "nativeOnMainActivitySuspended",
"("
")"
"V", reinterpret_cast<void*>(OnMainActivitySuspended) },
{ "nativeOnMainActivityResumed",
"("
")"
"V", reinterpret_cast<void*>(OnMainActivityResumed) },
};
const int kMethodsSystemMonitorSize = arraysize(kMethodsSystemMonitor);

if (env->RegisterNatives(g_SystemMonitor_clazz,
kMethodsSystemMonitor,
kMethodsSystemMonitorSize) < 0) {
LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
return false;
}

return true;
}

这个SystemMonitor_jni.h又是如何生成的呢?base.gyp中存在base_jni_headers这个target,其中包含了存在native代码的java文件,还include了jni_generator.gypi这个文件,其中调用了jni_generator.py这个脚本,读取Java源码,生成对应的jni代码。有兴趣的同学可以研读一下脚本,可以体会到脚本的威力。

native代码又是如何调用Java代码的呢,阅读SystemMonitor.java源码可以发现,isBatteryPower方法前面多了一个@CalledByNative标记,秘密就在这里。同样是jni_generator.py脚本,根据这个标记生成代码:

static jboolean Java_SystemMonitor_isBatteryPower(JNIEnv* env) {
/* Must call RegisterNativesImpl() */
DCHECK(g_SystemMonitor_clazz);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
env, g_SystemMonitor_clazz,
"isBatteryPower",

"("
")"
"Z",
&g_SystemMonitor_isBatteryPower);

jboolean ret =
env->CallStaticBooleanMethod(g_SystemMonitor_clazz,
method_id);
base::android::CheckException(env);
return ret;
}

Java/C++相互调用代码编写并不复杂,但google的工程师却做到了极致,将这一过程自动化,减少了重复敲乏味的代码,还避免了出错,值得我们学习。一代宗师叶问每次比武,只用摊、膀、扶“三板斧”,看似简单的招式,却运用的出神入化。

发表评论

电子邮件地址不会被公开。

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>