注:Android develop中给的的编码建议是:
之前文章提到过JNIEnv是线程相关的,即在每一个线程中都有一个JNIEnv指针,每个JNIEnv都是线程专有的,其他线程不能使用本线程中的JNIEnv.
一种比较常见的应用场景是:在native 层创建了线程,线程执行完后想将结果返回给java层,这时线程是不能用jni函数参数中的JNIEnv的,因为参数中的JNIEnv属于不同的线程.
分两种情况
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
JNIEnv* env = NULL;
jint result = -1;
javaVM = vm;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("ERROR: GetEnv failed\n");
goto bail;
}
assert(env != NULL);
//省略 ...
result = JNI_VERSION_1_4;
bail:
return result;
//先通过GetEnv去获取当前线程是否有JNIEnv, 如果没有再通过
//AttachCurrentThread将当前线程附加到 JavaVM
int status = javaVM->GetEnv((void**)&env, JNI_VERSION_1_4);
if (status < 0) {
javaVM->AttachCurrentThread(&env, NULL);
ALOGE("AttachCurrentThread");
}
...
if (status < 0) {
javaVM->DetachCurrentThread();
}
关于全局引用,Android MediaPlayer中有一个应用场景:
MediaPlayer中有好几个回调如onPrepared,onError等,都是native层回调java的postEventFromNative函数将消息传递上来的. natvie回调java需要获取到MediaPlayer的object,这个object是java层的MediaPlayer通过jni接口传递给native层的,属于局部引用,而native层发送消息可能是在不同的线程,所以必须要将object变成全局的引用.下面看下代码的实现:
frameworks/base/media/java/android/media/MediaPlayer.java
public MediaPlayer() {
...
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
*/
native_setup(new WeakReference<MediaPlayer>(this));
...
}
在MediaPlayer的构造函数里,调用了native层的函数native_setup,将自身的object弱引用传给native层
frameworks/base/media/jni/android_media_MediaPlayer.cpp
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
...
// create new listener and give it to MediaPlayer
sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
mp->setListener(listener);
...
}
jni函数中,创建了JNIMediaPlayerListener,将weak_this,即java层MediaPlayer的object传给JNIMediaPlayerListener.jni就是通过JNIMediaPlayerListener回调java的.再来看下JNIMediaPlayerListener
JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
{
// Hold onto the MediaPlayer class for use in calling the static method
// that posts events to the application thread.
jclass clazz = env->GetObjectClass(thiz);
if (clazz == NULL) {
ALOGE("Can't find android/media/MediaPlayer");
jniThrowException(env, "java/lang/Exception", NULL);
return;
}
mClass = (jclass)env->NewGlobalRef(clazz);
// We use a weak reference so the MediaPlayer object can be garbage collected.
// The reference is only used as a proxy for callbacks.
mObject = env->NewGlobalRef(weak_thiz);
}
可以看到,在构造函数中调用了mObject = env->NewGlobalRef(weak_thiz);创建了对MediaPlayer object的全局引用.u全局引用必须要主动地去释放它,可以猜测到释放的地方是在JNIMediaPlayerListener的析构函数
JNIMediaPlayerListener::~JNIMediaPlayerListener()
{
// remove global references
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->DeleteGlobalRef(mObject);
env->DeleteGlobalRef(mClass);
}