JNI原理(下)
一、注册JNI方法
接着上一篇,进入 JVM_NativeLoad 方法
JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
jstring javaFilename,
jobject javaLoader,
jstring javaLibrarySearchPath) {
...
std::string error_msg;
{
art::JavaVMExt* vm =
art::Runtime::Current()->GetJavaVM();
bool success = vm->LoadNativeLibrary(env,
filename.c_str(),
javaLoader,
javaLibrarySearchPath,
&error_msg);
...
}
该方法主要做了一件事:
调用Java虚拟机加载nativeLibrary
进入JavaVMExt中查看 LoadNativeLibrary 方法
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
const std::string& path,
jobject class_loader,
jstring library_path,
std::string* error_msg) {
...
SharedLibrary* library;
...
void* handle = android::OpenNativeLibrary(env,
runtime_->GetTargetSdkVersion(),
path_str,
class_loader,
library_path,
&needs_native_bridge,
error_msg);
...
bool created_library = false;
{
//通过创建一个 Android 的 Handle 并用其创建一个 SharedLibrary
std::unique_ptr<SharedLibrary> new_library(
new SharedLibrary(env,
self,
path,
handle,
needs_native_bridge,
class_loader,
class_loader_allocator));
MutexLock mu(self, *Locks::jni_libraries_lock_);
library = libraries_->Get(path);//通过路径获取library库
if (library == nullptr) {
library = new_library.release();
libraries_->Put(path, library);//将库放入 library 集合中,具体个数为动态链接库的个数
created_library = true;
}
}
...
//重点:
void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
if (sym == nullptr) {
VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
was_successful = true;
} else {
ScopedLocalRef<jobject> old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride()));
self->SetClassLoaderOverride(class_loader);
VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
//在我们要加载so库中查找JNI_OnLoad方法,如果没有系统就认为是静态注册方式进行的,直接返回true,代表so库加载成功。
//如果找到JNI_OnLoad就会调用JNI_OnLoad方法,JNI_OnLoad方法中一般存放的是方法注册的函数。
//所以如果采用动态注册就必须要实现JNI_OnLoad方法,否则调用java中声明的native方法时会抛出异常。
JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
int version = (*jni_on_load)(this, nullptr);
if (runtime_->GetTargetSdkVersion() != 0 && runtime_->GetTargetSdkVersion() <= 21) {
// Make sure that sigchain owns SIGSEGV.
EnsureFrontOfChain(SIGSEGV);
}
self->SetClassLoaderOverride(old_class_loader.get());
//对 version 进行判断
if (version == JNI_ERR) {
StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
} else if (JavaVMExt::IsBadJniVersion(version)) {
StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
path.c_str(), version);
} else {
was_successful = true;
}
}
bool JavaVMExt::IsBadJniVersion(int version) {
// We don't support JNI_VERSION_1_1. These are the only other valid versions.
return version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6;
}
该方法主要就是:
- 创建 Android 中的 Handle,并用 Handle 再创建一个 SharedLibrary ,并将其放入一个 SharedLibrary 的集合中
- 通过 SharedLibrary 的 FindSymbol(“JNI_OnLoad”, nullptr) 找到 JNI_Onload 方法并赋值给 void* 类型的 sym
- 将 sym 强转为 JNI_OnLoadFn 的方法指针(JNI_OnLoadFn 是 (JavaVM*, void*) 的方法别名)
- 通过 JNI_OnLoadFn 获取一个 int 类型的变量 version
- 对 version 进行判断,若 version 等于 JNI_ERR(-1) 或不等于 JNI_VERSION_1_2、JNI_VERSION_1_4、JNI_VERSION_1_6 中的任意一个,即为错误的。
二、Native方法的注册流程
打开Android源码中的MediaPlayer类,其中有
private native final void native_setup(Object mediaplayer_this);
其具体实现为 android_media_MediaPlayer.cpp 中的
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
类中还有一个 gMethod
static const JNINativeMethod gMethods[] = {
...
{"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup},
...
};
三个参数分别对应着
- Java中的方法
- Java中方法参数及返回值签名
- JNI中的方法
接下去寻找 gMethod 的调用,发现其被用于 register_android_media_MediaPlayer 方法中进行注册
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
static int register_android_media_MediaPlayer(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
register_android_media_MediaPlayer 的调用是在
JNI_OnLoad 中
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("ERROR: GetEnv failed\n");
goto bail;
}
assert(env != NULL);
//在此调用
if (register_android_media_MediaPlayer(env) < 0) {
ALOGE("ERROR: MediaPlayer native registration failed\n");
goto bail;
}
...
//此处的返回值与上文所说的三个正确返回值相对应
result = JNI_VERSION_1_4;
bail:
return result;
}
现在,一连串的源码分析就走通了:
- LoadNativeLibrary 方法中,SharedLibrary会通过 FindSymbol 调用 JNI_OnLoad
- JNI_OnLoad 会将 native 方法进行注册,并返回一个 version
- 若 version 为 JNI_VERSION_1_2、JNI_VERSION_1_4、JNI_VERSION_1_6 中的一个则表示注册成功
三、Native 方法注册的具体实现
接着刚刚 register_android_media_MediaPlayer 方法,继续寻找 registerNativeMethods 的实现
int AndroidRuntime::registerNativeMethods(JNIEnv* env,
const char* className, const JNINativeMethod* gMethods, int numMethods)
{
return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
又是一个方法调用,继续找 jniRegisterNativeMethods 的实现,这个方法在 JNIHelp.cpp 中
extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods)
{
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
//findClass 之后 赋值给 c
scoped_local_ref<jclass> c(env, findClass(env, className));
...
//c.get()就是获取刚刚 findClass 的类
if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0)
...
}
最终注册就是在 RegisterNatives 中
四、手写一个动态注册的 native 方法
1. 在Java中定义一个方法
public static native void diff(String path, String patternPath, int fileNum);
2. 在JNI中定义对应的方法
JNIEXPORT void JNICALL
jni_diff(JNIEnv *env, jclass type, jstring path_,
jstring patternPath_, jint fileNum) {
const char *path = (*env)->GetStringUTFChars(env, path_, 0);
const char *patternPath = (*env)->GetStringUTFChars(env, patternPath_, 0);
LogI("JNI Begin");
(*env)->ReleaseStringUTFChars(env, path_, path);
(*env)->ReleaseStringUTFChars(env, patternPath_, patternPath);
}
3. 定义一个 gMethod
static const JNINativeMethod gMethods[] = {
{"diff", "(Ljava/lang/String;Ljava/lang/String;I)V", (void *) jni_diff},
};
4. 定义一个注册方法
static int registerNative(JNIEnv *env) {
LogI("register native start");
jclass jclz = (*env)->FindClass(env, "yourpackage/className");
if (jclz == NULL) {
LogI("class is null");
}
if ((*env)->RegisterNatives(env, jclz, gMethods, NELEM(gMethods)) < 0) {
LogI("register failed");
};
LogI("register native success");
return JNI_TRUE;
}
5. 在JNI_OnLoad中实现注册方法
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
jint result = -1;
if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_4) != JNI_OK) {
LogI("ERROR: GetEnv failed\n");
goto bail;
}
assert(env != NULL);
if (registerNative(env) < 0) {
LogI("ERROR: native registration failed");
goto bail;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
LogI("register native completed");
bail:
return result;
}
6. 运行打印结果
I/TAG: register native start
I/TAG: register native success
I/TAG: register native completed