建议用用在设备休眠的时候第三方视频播放器主动调用VideoView.suspend()方法
我们很多第三方播放器应用在休眠唤醒的时候处理的不是很棒,造成很多平台不能很好的兼容,最进在公司就处理过此类问题。
默认情况下,当我们点击power键的时候,我们客户端播放器会和服务端断开连接,此时服务端的Client会析构,当再次返回时会重新唤醒时服务端会重新创建服务端,读取上次保存的位置,开始播放或者待用户确认后开始播放,这样做会节约功耗,你不希望用户用你产品后本来可以撑一天的手机,现在只能用半天了吧~
废话少说,直接把图库的行为给大家看下:
SEP 1 .MovieActivity
@Override public void onPause() { mPlayer.onPause(); super.onPause(); }
当点击power键的时候,会调用movieActitivy的onPause方法
SEP 2. MoviePlayer
public void onPause() { mHasPaused = true; mHandler.removeCallbacksAndMessages(null); mVideoPosition = mVideoView.getCurrentPosition(); mBookmarker.setBookmark(mUri, mVideoPosition, mVideoView.getDuration()); mVideoView.suspend(); mResumeableTime = System.currentTimeMillis() + RESUMEABLE_TIMEOUT; }
保存状态,保存为书签,调用VideoView的挂起方法,其真正调用的是VideoView的release方法
SEP 3 .ViewoView
/* * release the media player in any state */ private void release(boolean cleartargetstate) { if (mMediaPlayer != null) { mMediaPlayer.reset(); mMediaPlayer.release(); mMediaPlayer = null; mCurrentState = STATE_IDLE; if (cleartargetstate) { mTargetState = STATE_IDLE; } } }
SEP4.MediaPlay.java
public void release() { stayAwake(false); updateSurfaceScreenOn(); mOnPreparedListener = null; mOnBufferingUpdateListener = null; mOnCompletionListener = null; mOnSeekCompleteListener = null; mOnErrorListener = null; mOnInfoListener = null; mOnVideoSizeChangedListener = null; mOnTimedTextListener = null; _release(); } private native void _release(); ... ... public void reset() { stayAwake(false); _reset(); // make sure none of the listeners get called anymore mEventHandler.removeCallbacksAndMessages(null); } private native void _reset();
先说reset:
SEP5.android_media_MediaPlayer.cpp
// 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) 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);
}
}
}
}
SEP6.MediaPlayer.cpp
void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj) { ... ... // Allows calls from JNI in idle state to notify errors if (!(msg == MEDIA_ERROR && mCurrentState == MEDIA_PLAYER_IDLE) && mPlayer == 0) { ALOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2); if (locked) mLock.unlock(); // release the lock when done. return; } ... ... }
再回头看,release
SEP7.android_media_MediaPlayer.cpp
static void android_media_MediaPlayer_release(JNIEnv *env, jobject thiz) { ALOGV("release"); decVideoSurfaceRef(env, thiz); sp<MediaPlayer> mp = setMediaPlayer(env, thiz, 0); if (mp != NULL) { // this prevents native callbacks after the object is released mp->setListener(0); mp->disconnect(); } }
SEP8.MediaPlayer.cpp
void MediaPlayer::disconnect()
{
ALOGV("disconnect");
sp<IMediaPlayer> p;
{
Mutex::Autolock _l(mLock);
p = mPlayer;
mPlayer.clear();
}
if (p != 0) {
p->disconnect();
}
}
这里的IMediaPlayer的指针指向的就是MdiaPlayerService::Client;智能指针clear就会调用其析构函数,然后断开与服务端的连接
SEP9.MediaPlayerService.cpp
MediaPlayerService::Client::~Client() { ALOGV("Client(%d) destructor pid = %d", mConnId, mPid); mAudioOutput.clear(); wp<Client> client(this); disconnect(); mService->removeClient(client); } void MediaPlayerService::Client::disconnect() { ALOGV("disconnect(%d) from pid %d", mConnId, mPid); // grab local reference and clear main reference to prevent future // access to object sp<MediaPlayerBase> p; { Mutex::Autolock l(mLock); p = mPlayer; } mClient.clear(); mPlayer.clear(); // clear the notification to prevent callbacks to dead client // and reset the player. We assume the player will serialize // access to itself if necessary. if (p != 0) { p->setNotifyCallback(0, 0); #if CALLBACK_ANTAGONIZER ALOGD("kill Antagonizer"); mAntagonizer->kill(); #endif p->reset(); } disconnectNativeWindow(); IPCThreadState::self()->flushCommands(); }
关闭和服务端的连接流程基本就是这样的。返回流程后续补上。