接着上一篇文章继续研究MeidaPlayer
三个入手点:
1 private static native final void native_init();
2 private native final void native_setup(Object mediaplayer_this);
3 private native void nativeSetDataSource(IBinder httpServiceBinder, String path, String[] keys, String[] values) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
jni实现在frameworks\base\media\jni\android_media_MediaPlayer.cpp
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
JNIEnv* env = NULL;
jint result = -1;
。。。
if (register_android_media_MediaPlayer(env) < 0) {
ALOGE("ERROR: MediaPlayer native registration failed\n");
goto bail; }
}
if语句会调用如下函数
// This function only registers the native methods
static int register_android_media_MediaPlayer(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
static JNINativeMethod gMethods[] = {
{
"nativeSetDataSource",//对应java文件中的本地方法名
"(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
"[Ljava/lang/String;)V",//未知
(void *)android_media_MediaPlayer_setDataSourceAndHeaders//JNI方法名
},
{"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
{"_setDataSource", "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
{"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer_setVideoSurface},
。。。
{"native_init", "()V", (void *)android_media_MediaPlayer_native_init},
{"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup},
};
从上面知道native_init、native_setup、nativeSetDataSource分别对应的JNI方法名
1、native_init()
// This function gets some field IDs, which in turn causes class initialization.
// It is called from a static block in MediaPlayer, which won't run until the
// first time an instance of this class is used.
static void
android_media_MediaPlayer_native_init(JNIEnv *env)
{
jclass clazz;
clazz = env->FindClass("android/media/MediaPlayer");
if (clazz == NULL) {
return;
}
fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
if (fields.context == NULL) {
return;
}
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.post_event == NULL) {
return;
}
fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");
if (fields.surface_texture == NULL) {
return;
}
env->DeleteLocalRef(clazz);
clazz = env->FindClass("android/net/ProxyInfo");
if (clazz == NULL) {
return;
}
fields.proxyConfigGetHost =
env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;");
fields.proxyConfigGetPort =
env->GetMethodID(clazz, "getPort", "()I");
fields.proxyConfigGetExclusionList =
env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");
env->DeleteLocalRef(clazz);
gPlaybackParamsFields.init(env);
gSyncParamsFields.init(env);
}
//todo 具体实现暂时先不研究
2、native_setup()
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
ALOGV("native_setup");
sp<MediaPlayer> mp = new MediaPlayer();
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
// create new listener and give it to MediaPlayer
sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
mp->setListener(listener);
// Stow our new C++ MediaPlayer in an opaque field in the Java object.
setMediaPlayer(env, thiz, mp);
}
接下来一句一句分析上述代码
sp<MediaPlayer> mp = new MediaPlayer();
这里创建一个MediaPlayer强指针对象。(此处涉及智能指针相关知识,另具体研究)
sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this); mp->setListener(listener);
JNIMediaPlayerListener是android_media_MediaPlayer.cpp中的内部类
class JNIMediaPlayerListener: public MediaPlayerListener { public: JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz); ~JNIMediaPlayerListener(); virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL); private: JNIMediaPlayerListener(); jclass mClass; // Reference to MediaPlayer class jobject mObject; // Weak ref to MediaPlayer Java object to call on }; 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); }
JNIMediaPlayerListener继承自MediaPlayerListener类,MediaPlayerListener类是frameworks\av\include\media\mediaplayer.h文件中的内部类。
class MediaPlayerListener: virtual public RefBase
{
public:
virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) = 0;
};
只有一个纯虚函数,在JNIMediaPlayerListener中实现,如下
void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (obj && obj->dataSize() > 0) {
jobject jParcel = createJavaParcelObject(env);
if (jParcel != NULL) {
Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
nativeParcel->setData(obj->data(), obj->dataSize());
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, jParcel);
env->DeleteLocalRef(jParcel);
}
} else {
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, NULL);
}
if (env->ExceptionCheck()) {
ALOGW("An exception occurred while notifying an event.");
LOGW_EX(env);
env->ExceptionClear();
}
}//todo 暂不分析
继续
setMediaPlayer(env, thiz, mp);
static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{//mz2
Mutex::Autolock l(sLock);//同步锁
sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context);//获取旧指针
if (player.get()) {//刚开始不为NULL,计数+1
player->incStrong((void*)setMediaPlayer);
}
if (old != 0) {//刚开始应该为0
old->decStrong((void*)setMediaPlayer);
}
env->SetLongField(thiz, fields.context, (jlong)player.get());//存入MediaPlayer
return old;
}
3、nativeSetDataSource()
httpServiceBinderObj在java层的类型是IBinder 值为:MediaHTTPService.createHttpS erviceBinderIfNecessary(path)path是String 传入的URL
keys是String[] 默认为null
values是String[] 默认为null
static void android_media_MediaPlayer_setDataSourceAndHeaders( JNIEnv *env, jobject thiz, jobject httpServiceBinderObj, jstring path, jobjectArray keys, jobjectArray values) { sp<MediaPlayer> mp = getMediaPlayer(env, thiz); if (mp == NULL ) {//此时不为null jniThrowException(env, "java/lang/IllegalStateException", NULL); return; } if (path == NULL) {//此时不为null jniThrowException(env, "java/lang/IllegalArgumentException", NULL); return; } const char *tmp = env->GetStringUTFChars(path, NULL); if (tmp == NULL) { // Out of memory return; } ALOGV("setDataSource: path %s", tmp); String8 pathStr(tmp); env->ReleaseStringUTFChars(path, tmp); tmp = NULL; // We build a KeyedVector out of the key and val arrays KeyedVector<String8, String8> headersVector; if (!ConvertKeyValueArraysToKeyedVector( env, keys, values, &headersVector)) { return; } sp<IMediaHTTPService> httpService; if (httpServiceBinderObj != NULL) { sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj); httpService = interface_cast<IMediaHTTPService>(binder); } status_t opStatus = mp->setDataSource( httpService, pathStr, headersVector.size() > 0? &headersVector : NULL); process_media_player_call( env, thiz, opStatus, "java/io/IOException", "setDataSource failed." ); }
继续
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz) { Mutex::Autolock l(sLock); MediaPlayer* const p = (MediaPlayer*)env->GetLongField(thiz, fields.context); return sp<MediaPlayer>(p); }继续
sp<IMediaHTTPService> httpService; if (httpServiceBinderObj != NULL) { sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj); httpService = interface_cast<IMediaHTTPService>(binder); }
继续
status_t opStatus = mp->setDataSource( httpService, pathStr, headersVector.size() > 0? &headersVector : NULL);
这里调用的是MediaPlayer类中的setDataSource
继续
process_media_player_call( env, thiz, opStatus, "java/io/IOException", "setDataSource failed." );
// If exception is NULL and opStatus is not OK, this method sends an error // event to the client application; otherwise, if exception is not NULL and // opStatus is not OK, this method throws the given exception to the client // application. static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message) { if (exception == NULL) { // Don't throw exception. Instead, send an event. if (opStatus != (status_t) OK) { sp<MediaPlayer> mp = getMediaPlayer(env, thiz); if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0); } } else { // Throw exception! if ( opStatus == (status_t) INVALID_OPERATION ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); } else if ( opStatus == (status_t) BAD_VALUE ) { jniThrowException(env, "java/lang/IllegalArgumentException", NULL); } else if ( opStatus == (status_t) PERMISSION_DENIED ) { jniThrowException(env, "java/lang/SecurityException", NULL); } else if ( opStatus != (status_t) OK ) { if (strlen(message) > 230) { // if the message is too long, don't bother displaying the status code jniThrowException( env, exception, message); } else { char msg[256]; // append the status code to the message sprintf(msg, "%s: status=0x%X", message, opStatus); jniThrowException( env, exception, msg); } } } }//todo 初步看是个异常处理的功能,具体另分析
总结,本章主要分析JNI层源码,下一章开始分析c++源码。