• android媒体框架之图库行为


    建议用用在设备休眠的时候第三方视频播放器主动调用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();
    }
    


    关闭和服务端的连接流程基本就是这样的。返回流程后续补上。

  • 相关阅读:
    ssh远程执行命令
    华为交换机配置命令总结
    dmidecode查看设备硬件信息
    tcpdump高级过滤技巧
    ifconfig 下面的一些字段(errors, dropped, overruns)
    awk 高级技巧
    intel82599在centos6.5下编译安装
    非默认安装目录下mysql数据的导出与导入
    bash中使用mysql中的update命令
    mysql查看修改字符集
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3105087.html
Copyright © 2020-2023  润新知