AndroidAndroid知识

MediaPlayer

2016-08-18  本文已影响2362人  哦丶那么棒

(一)android中的媒体

android多媒体框架支持多种媒体类型,包括音频、视频、图片。使用的媒体文件可以来自于资源文件,系统文件或者sd卡,网络。
注意:如果你在播放一个视频,途中来电,视频将停止播放

Mediaplayer:
用来播放视频和音频
AudioManager:
用来管理音频输出和音频来源

如果播放的资源来自网络请加上网络权限
<uses-permission android:name="android.permission.INTERNET" />
如果你播放时保持屏幕常亮或者处理器休眠请添加如下方法并且添加相应的权限
MediaPlayer.setScreenOnWhilePlaying()
MediaPlayer.setWakeMode()
<uses-permission android:name="android.permission.WAKE_LOCK" />

(二)使用MediaPlayer

MediaPlayer准备资源调用prepare()时,会执行一段稍长的时间,因为它在解码媒体数据,如果解码时间过长那么会出现主线程阻塞,从而触发ANR异常,导致程序运行很慢,所以框架提供了prepareAsync()异步准备方法并提供资源准备监听,当资源准备完成会触发MediaPlayer.OnPreparedListener的onPrepared()方法。

2.管理状态

当你创建一个MediaPlayer对象时它处于ldle state(闲置状态),这个时候你应该调用setDataSource()设置资源到达Initialized state(初始化状态),此时你必须调用prepare()或者prepareAsync()准备资源,当资源准备完毕时(Prepared状态),你就可以调用start()开始播放。详细流程如下图


mediaplayer_state_diagram.gif

3.释放

因为MediaPlayer消耗的系统资源太多了,当不需要播放时你应该调用release()释放系统资源,请参考以下方式来做。
mediaPlayer.release();
mediaPlayer = null;

(三)在Service中使用MediaPlayer播放音乐

在Service的操作都默认在主线程上面执行,如果需要进行繁复耗时操作必须要异步进行,比如在主线程中使用MediaPlayer时,应该调用prepareAsync()而不是PrePare(),并且要设置资源准备监听MediaPlayer.OnPreparedListener()来接受资源准备完成的通知。
public class MyService extends Service implements MediaPlayer.OnPreparedListener {
private static final ACTION_PLAY = "com.example.action.PLAY";
MediaPlayer mMediaPlayer = null;
public int onStartCommand(Intent intent, int flags, int startId) {
...
if (intent.getAction().equals(ACTION_PLAY)) {
mMediaPlayer = ... // initialize it here
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.prepareAsync(); // prepare async to not block main thread
}
}
/** Called when MediaPlayer is ready */
public void onPrepared(MediaPlayer player) {
player.start();
}
}

在某种情况下音频播放时可能会报错,为了处理这种情况我们必须设置MediaPlayer.OnErrorListener监听,来恢复之前的状态,在恢复之前记得先释放掉它

当音频在后台播放时,设备可能进入到休眠状态,此时系统会关闭一些不必要的资源,包括CPU和wifi等等...如果想要在后台播放或者缓冲音乐并且不想被系统干扰必须使用锁,并且在paused或者stopped状态下释放它
mMediaPlayer = new MediaPlayer();// ... other initialization here ...
mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
上面代码只会确保cpu一直工作,如果你使用wifi播放流媒体,你还需要持有wifi锁
WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE)).createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
wifiLock.acquire();
当你停止播放或者不再需要连接网络释放
wifiLock.release();

Service通常是用来执行耗时操作,比如同步数据,下载文件。这些操作通常不需要与用户交互,所以可以运行在后台不让用户知道,但是使用Service播放音乐显然必须与用户进行互动,在这种情况下我们需要启动前台服务,前台服务在系统中享有很高的优先级,系统基本不会杀死这个Service。Service必须提供notification来提示用户,并通过notification与用户交互

android是一个支持多任务的系统,这给使用audio的程序带来了一定的风险,因为可能几个程序会来竞争音频输出设备,在android2.2之前没有内置的机制来处理这个问题,导致体验很差。比如用户在听音乐时,另一个应用需要向用户提示一个非常重要的通知,但音乐声盖过了通知音,导致用户错失了最佳接收提示的时间,为了协调设备的音频输出,android提出了Audio Focus机机制,获取audio focus必须调用AudioManager的requestAudioFocus()方法。
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// could not get audio focus.
}
requestAudioFocus的第一个参数是AudioManager.OnAudioFocusChangeListener,这个接口onAudioFocusChange()方法将会在audio focus改变时调用举个例子:
class MyService extends Service implements AudioManager.OnAudioFocusChangeListener {
// ....
public void onAudioFocusChange(int focusChange) {
// Do something based on focus change...
}
}
focusChange参数值如下
AUDIOFOCUS_GAIN:获取audio focus
AUDIOFOCUS_LOSS:失去audio focus很长一段时间,必须停止所有的audio播放,清理资源
AUDIOFOCUS_LOSS_TRANSIENT:暂时失去audio focus,但是很快就会重新获得,在此状态应该暂停所有音频播放,但是不能清除资源
AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:暂时失去 audio focus,但是允许持续播放音频(以很小的声音),不需要完全停止播放。
实例:
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
// resume playback
if (mMediaPlayer == null) initMediaPlayer();
else if (!mMediaPlayer.isPlaying()) mMediaPlayer.start();
mMediaPlayer.setVolume(1.0f, 1.0f);
break;
case AudioManager.AUDIOFOCUS_LOSS:
// Lost focus for an unbounded amount of time: stop playback and release media player
if (mMediaPlayer.isPlaying()) mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// Lost focus for a short time, but we have to stop
// playback. We don't release the media player because playback
// is likely to resume
if (mMediaPlayer.isPlaying()) mMediaPlayer.pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// Lost focus for a short time, but it's ok to keep playing
// at an attenuated level
if (mMediaPlayer.isPlaying()) mMediaPlayer.setVolume(0.1f, 0.1f);
break;
}
}

一个MediaPlayer会消耗很多系统资源,在必要的时候才维持它的实例,一旦不需要了及时调用release()来释放资源。调用这个方法要比等待垃圾回收要好很多,因此在使用service时,必须复写onDestyoy()方法来确保MediaPlayer得到释放:
public class MyService extends Service {
MediaPlayer mMediaPlayer;
// ...
@Override
public void onDestroy() {
if (mMediaPlayer != null) mMediaPlayer.release();
}
}
除了在Service生命周期结束时释放资源,还可以在失去audio focus释放资源

当用户插着耳机听音乐突然拔掉耳机,如果没有对以上意图进行处理,音频将会通过外部扬声器来播放音频,这也许不会是用户想要的,所以我们必须对此处理。
1.在manifest中注册一个广播
<receiver android:name=".MusicIntentReceiver">
<intent-filter>
<action android:name="android.media.AUDIO_BECOMING_NOISY" />
</intent-filter>
</receiver>
2.注册MusicIntentReceiver 广播来处理这个intent
public class MusicIntentReceiver implements android.content.BroadcastReceiver {
@Override
public void onReceive(Context ctx, Intent intent) {
if (intent.getAction().equals( android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
// signal your service to stop playback
// (via an Intent, for instance)
}
}
}

通过ContentResolver来获取外部媒体文件
ContentResolver contentResolver = getContentResolver();
Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, null, null, null, null);
if (cursor == null) {
// query failed, handle error.
} else if (!cursor.moveToFirst()) {
// no media on the device
} else {
int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE);
int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);
do {
long thisId = cursor.getLong(idColumn);
String thisTitle = cursor.getString(titleColumn);
// ...process entry...
} while (cursor.moveToNext());
}
配合MediaPlayer使用
long id = /* retrieve it from somewhere */;
Uri contentUri = ContentUris.withAppendedId( android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setDataSource(getApplicationContext(), contentUri);
// ...prepare and start...

上一篇下一篇

猜你喜欢

热点阅读