建議用用在裝置休眠的時候第三方視頻播放器主動調用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 voidandroid_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();}
關閉和服務端的串連流程基本就是這樣的。返迴流程後續補上。