网站监测岚山网站建设报价
- 作者: 五速梦信息网
- 时间: 2026年04月20日 07:55
当前位置: 首页 > news >正文
网站监测,岚山网站建设报价,深圳全网推广方案,临海手机网站设计上一节了解了MediaPlayer api的使用#xff0c;这一节就我们将会了解MediaPlayer的生命周期与api使用细节。 1、MediaPlayer生命周期 MediaPlayer.java 一开始有对生命周期的描述#xff0c;这里对这些内容进行翻译#xff1a; MediaPlayer 是线程不安全的#xff0c;创建… 上一节了解了MediaPlayer api的使用这一节就我们将会了解MediaPlayer的生命周期与api使用细节。 1、MediaPlayer生命周期 MediaPlayer.java 一开始有对生命周期的描述这里对这些内容进行翻译 MediaPlayer 是线程不安全的创建以及调用应该在同一个线程中如果需要使用Callback那么线程必须要有一个Looper调用 new 或者 reset 后MediaPlayer进入 Idle 状态release 调用后进入到 End 状态这两个状态之间就是 MediaPlayer 对象的生命周期调用 new 或者 reset都会进入Idle 状态但是它们仍然是有区别的。getDuration 等方法在Idle 状态下调用会出现error如果是new之后调用虽然会返回error但是error并不是由内部播放器引擎上抛的播放器状态仍然保持在Idle 状态如果刚好是在reset之后调用getDuration方法那么内部播放器引擎会上抛error事件并且把播放器状态置为Error如果MediaPlayer对象不再需要被使用推荐调用release方法让内部播放器引擎使用的资源能够被释放一旦进入了 End 状态MediaPlayer对象将不能再被使用并且不能再被切换到其他状态如果仍要使用需要重新new一个对象进入Idle状态。有很多原因会造成播放失败比如不支持的audio/video格式、分辨率过高、streaming超时等这些错误会让播放器进入Error状态并且上抛事件如果要继续使用当前MediaPlayer对象可以调用reset使其进入到Idle状态setDataSource会让播放器从Idle进入Loaded状态如果在其他状态调用则会抛出IllegalStateException错误prepare会让播放器从Loaded状态进入到Prepared状态如果调用的是prepareAsync则会先进入中间状态Preparing在除了Loaded和Stopped状态外调用prepare方法都是非法的会抛出IllegalStateException错误start方法会让播放器从Prepared状态进入到Started状态可以用isPlaying判断播放器当前状态是否在Started状态如果已经进入到Started状态则再调用start不会有任何影响pause方法会让播放器进入到Paused状态从Started进入到Paused或者反过来的过程都是异步的如果已经进入到Paused状态则再调用pause不会有任何影响在Paused状态下重新调用start将会恢复播放进入到Started状态stop会让播放器从Started、Paused、Prepared、PlaybackCompleted状态进入到Stopped状态一旦进入到Stopped状态那么必须要调用prepare重新进入Prepared状态才能够重新进行播放同样的如果已经进入到Stopped状态则再调用stop不会有任何影响seekTo方法是异步的调用完成后会有callback事件onSeekComplete上抛seekTo可以在Started、Paused、Prepared、PlaybackCompleted状态下调用当播放结束时如果setLooping设置为true那么播放器将会保持在Started状态如果setLooping设置为false那么播放器将会进入到PlaybackCompleted状态在PlaybackCompleted状态下调用start方法将会从头开始重新播放并且进入到Started状态。 2、异常处理 从上面我们可以了解到MediaPlayer维护了一套状态机并且调用MediaPlayer方法时会检查当前调用是否是非法的这套状态机机制在 mediaplayer.cpp 中。 MediaPlayer 定义了如下状态: enum media_player_states {MEDIA_PLAYER_STATE_ERROR 0,MEDIA_PLAYER_IDLE 1 0,MEDIA_PLAYER_INITIALIZED 1 1,MEDIA_PLAYER_PREPARING 1 2,MEDIA_PLAYER_PREPARED 1 3,MEDIA_PLAYER_STARTED 1 4,MEDIA_PLAYER_PAUSED 1 5,MEDIA_PLAYER_STOPPED 1 6,MEDIA_PLAYER_PLAYBACK_COMPLETE 1 7 };要注意的是End状态表示native MediaPlayer对象已经销毁了所以它并没有真正的 End 状态。 MediaPlayer.java 中还有一段关于函数在什么状态下调用是有效的什么状态下调用是无效的的表格这里挑出一些进行翻译。 序号方法有效状态无效状态下调用1getAudioSessionIdany2getCurrentPositionInitialized, Prepared, Started, Paused, Stopped, PlaybackCompletedError3getDurationPrepared, Started, Paused, Stopped, PlaybackCompletedError4getVideoHeightany5isPlayingany6pauseStarted, Paused, PlaybackCompletedError7prepareInitialized, Stopped异常8prepareAsyncInitialized, Stopped异常9releaseany10resetany11seekToPrepared, Started, Paused, PlaybackCompletedError12setDataSourceIdle异常13setDisplayany15setSurfaceany16setVideoScalingModeInitialized, Prepared, Started, Paused, Stopped, PlaybackCompleted17setLoopingIdle, Initialized, Stopped, Prepared, Started, Paused, PlaybackCompleted18startPrepared, Started, Paused, PlaybackCompletedError19stopPrepared, Started, Stopped, Paused, PlaybackCompletedError 从以上表格我们可以发现非法状态下调用prepare、prepareAsync、setDataSource会抛异常中止程序运行调用start、pause等方法会将状态置为 Error具体程序如何反应需要看我们的OnErrorListener是如何处理的 case MEDIA_ERROR:Log.e(TAG, Error ( msg.arg1 , msg.arg2 ));boolean error_was_handled false;OnErrorListener onErrorListener mOnErrorListener;if (onErrorListener ! null) {error_was_handled onErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2);}{mOnCompletionInternalListener.onCompletion(mMediaPlayer);OnCompletionListener onCompletionListener mOnCompletionListener;if (onCompletionListener ! null ! error_was_handled) {onCompletionListener.onCompletion(mMediaPlayer);}}stayAwake(false);return;3、对new、release、reset的一些理解 相关代码路径 MediaPlayer.javaandroid_media_MediaPlayer.cppmediaplayer.cpp 3.1、new 在看正式的 new MediaPlayer 流程前我们先要注意MediaPlayer.java中的如下代码段 static {System.loadLibrary(media_jni);native_init();}它会加载media_jni.so并执行native函数 android_media_MediaPlayer_native_init 。native_init会获取java类中的postEventFromNative方法IDmNativeContext、mNativeSurfaceTexture等字段的ID获取到的ID会存储在静态变量fields_t中后期可以通过这些ID找到Java对象中对应的成员获取其存储的值或者向其中存储值。 static fields_t fields;fields.context env-GetFieldID(clazz, mNativeContext, J); fields.post_event env-GetStaticMethodID(clazz, postEventFromNative, (Ljava/lang/Object;IIILjava/lang/Object;)V); fields.surface_texture env-GetFieldID(clazz, mNativeSurfaceTexture, J);接下来看new MediaPlayer创建一个对象会做哪些事情 private MediaPlayer(int sessionId) {…try (ScopedParcelState attributionSourceState attributionSource.asScopedParcelState()) {native_setup(new WeakReferenceMediaPlayer(this), attributionSourceState.getParcel());} }核心是调用native_setup方法需要将自身的弱引用对象作为参数向下传递 static void android_media_MediaPlayer_native_setup(JNIEnv env, jobject thiz, jobject weak_this,jobject jAttributionSource) {Parcel parcel parcelForJavaObject(env, jAttributionSource);android::content::AttributionSourceState attributionSource;attributionSource.readFromParcel(parcel);// 创建MediaPlayer native对象spMediaPlayer mp spMediaPlayer::make(attributionSource);// 创建并注册Callback对象spJNIMediaPlayerListener listener new JNIMediaPlayerListener(env, thiz, weak_this);mp-setListener(listener);// 将MediaPlayer对象存储到Java对象中setMediaPlayer(env, thiz, mp); }native_setup干了3件事 创建MediaPlayer native对象用传下来的弱引用对象创建Listener对象用于Callback调用将MediaPlayer native对象指针到Java对象中 来看看如何存储MediaPlayer native指针的 static spMediaPlayer setMediaPlayer(JNIEnv* env, jobject thiz, const spMediaPlayer player) {Mutex::Autolock l(sLock);spMediaPlayer old (MediaPlayer)env-GetLongField(thiz, fields.context);// 手动增加强引用计数if (player.get()) {player-incStrong((void)setMediaPlayer);}// 检查mNativeContext中存储的MediaPlayer对象if (old ! 0) {old-decStrong((void*)setMediaPlayer);}// 将MediaPlayer的地址存储到mNativeContextenv-SetLongField(thiz, fields.context, (jlong)player.get());return old; }这里有3个步骤 首先要增加MediaPlayer native对象的强引用计数检查mNativeContext中存储的地址是否为NULL如果不为NULL则需要销毁存储的MediaPlayer对象将新的MediaPlayer对象存储到mNativeContext中。 虽然我们会把MediaPlayer对象存储到Java字段中但是其引用计数在setMediaPlayer中仍为1如不增加引用计数出了当前作用域native对象将自动销毁。 3.2、reset reset的作用是重置当前MediaPlayer对象使其恢复到new的状态。我们来看下reset实现 status_t MediaPlayer::reset_l() {mLoop false;if (mCurrentState MEDIA_PLAYER_IDLE) return NO_ERROR;// 1.阻止 MEDIA_PREPARED 事件上抛mPrepareSync false;if (mPlayer ! 0) {// 2.调用内部实现的resetstatus_t ret mPlayer-reset();if (ret ! NO_ERROR) {ALOGE(reset() failed with return code (%d), ret);mCurrentState MEDIA_PLAYER_STATE_ERROR;} else {// 2.调用内部实现的disconnectmPlayer-disconnect();mCurrentState MEDIA_PLAYER_IDLE;}// 3.销毁内部实现mPlayer 0;return ret;}// 4.清除设置clear_l();return NO_ERROR; }reset主要会做3件事情 如果当前状态是Preparing那么会将 mPrepareSync 置为fasle阻止 MEDIA_PREPARED 事件上抛具体内部如何操作后续再研究调用 MediaPlayer 内部实例的 disconnect 方法断开连接如果处在播放状态将会停止播放这点后续再研究销毁 MediaPlayer 内部实例 3.3、release release会直接销毁掉MediaPlayer native对象一旦调用release当前MediaPlayer java对象将不能够再被使用 MediaPlayer::~MediaPlayer() {ALOGV(destructor);if (mAudioAttributesParcel ! NULL) {delete mAudioAttributesParcel;mAudioAttributesParcel NULL;}AudioSystem::releaseAudioSessionId(mAudioSessionId, (pid_t)-1);disconnect();IPCThreadState::self()-flushCommands(); }MediaPlayer 的析构函数同样也会调用内部实例的 disconnect 方法断开连接之后会销毁 MediaPlayer 内部实例这两点和reset是相同的。我们在想要结束activity可以先调用reset再调用release也可以直接调用release来释放资源。
- 上一篇: 网站架设工具17网站一起做网店 新塘
- 下一篇: 网站检测报告哪里做dede小视频网站源码
相关文章
-
网站架设工具17网站一起做网店 新塘
网站架设工具17网站一起做网店 新塘
- 技术栈
- 2026年04月20日
-
网站架设的结构没有公司 接单做网站
网站架设的结构没有公司 接单做网站
- 技术栈
- 2026年04月20日
-
网站架构文案抖音代运营保证金
网站架构文案抖音代运营保证金
- 技术栈
- 2026年04月20日
-
网站检测报告哪里做dede小视频网站源码
网站检测报告哪里做dede小视频网站源码
- 技术栈
- 2026年04月20日
-
网站检测网站开发待遇怎么样
网站检测网站开发待遇怎么样
- 技术栈
- 2026年04月20日
-
网站简繁体转换 js公司装修款怎么入账
网站简繁体转换 js公司装修款怎么入账
- 技术栈
- 2026年04月20日
