Android工作总结之如何做一个优秀的MediaPlayer音频播放器
Android工作总结之如何做一个优秀的MediaPlayer音频播放器
参考:
http://developer.android.com/guide/topics/media/mediaplayer.html
最近接触了项目播放的模块,就来总结下吧.
1.基础工作
声明权限
"android.permission.INTERNET" />
唤醒权限(后面会用到)
"android.permission.WAKE_LOCK" />
顺便说下AudioManager.setAudioStream(STREAM_RING,STREAM_RING,STREAM_VOICE_CALL等等)就是设置MediaPlayer对应的流.我们平时按音量键弹出的音量条有时候是铃声,有时候是媒体,说明不同的流对应独立的音量设置.系统会根据当前播放的流弹出对应的音量条.
关于prepare().谷歌推荐做法是调用prepareAsync()然后设置OnPrepareListener在onPrepare()里进行播放.因为解码一些比较大的文件的时候会花一些时间从而导致ANR.其实这个也不一定,灵活运用就好.
2.状态处理
一句话,在错误的状态做错误的处理会出错.呵呵…..听起来像废话
先上一张图
setDateSource()–>initialState
prepare() or prepareAsync() –>PrepareState
之后你就可以解锁很多姿势比如start() pause() seekTo()等
调用 stop()之后你就需要重新初始化了,这时候调用start pause等就会报错.
推荐的释放方式:
mediaPlayer.release();mediaPlayer = null;
尽量在Service中控制Mediaplayer,可以避免很多问题,比如横竖屏的切换.
setOnErrorListener()一般用来写reset Mediaplayer的代码.
3.唤醒MediaPlayer
当我们手机在后台运行app时,一旦进入sleep状态,就会做一些省电的措施比如关闭wifi,降低CPU运行效率.这样MediaPlayer就不能正常播放.安卓提供了一些方法能保证MediaPlayer正常播放.
唤醒CPU:
MediaPlayer.setWakeMode().调用这个方法,会在播放的时候hold住这个锁,在release或pause时候释放这个锁.
mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
唤醒wifi:
网络播放需要保持wifi开启.
WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE)) .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");wifiLock.acquire();
相比上一个锁,这个锁是需要手动释放的
wifiLock.release();
4.在前台运行:
也就是做成一个Notification.
PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(getApplicationContext(), MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);Notification notification = new Notification();notification.tickerText = text;notification.icon = R.drawable.play0;notification.flags |= Notification.FLAG_ONGOING_EVENT;notification.setLatestEventInfo(getApplicationContext(), "MusicPlayerSample", "Playing: " + songName, pi);startForeground(NOTIFICATION_ID, notification);//不用了的话就调用stopForeground(true)
这里的知识点都跟Service,Notification有关了,不打算在这篇里详讲.就好比面向对象编程的其中一个原则——单一职责原则:一个类设计的时候最好只做一件事(Ps:这特么跟写文章有毛的关系,明明就是懒好吗!).
5.焦点处理AudioFocus
这个功能点是用起来最高大上的.
首先说一下,手机里有很多MediaPlayer播放器,各个app之间是没有代码的交集的.我们在播放音乐的过程中收到短信提示音,播放器会调低音量,收到来电会暂停播放,这些就是通过焦点机制完成的.
请求焦点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. }
this指的是实现OnAudioFocusChangeListene的类
class MyService extends Service implements AudioManager.OnAudioFocusChangeListener { public void onAudioFocusChange(int focusChange) { // Do something based on focus change... } }
- focusChange类型:
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT 推荐做法会让其他播放器暂停pause playback(对应AUDIOFOCUS_LOSS_TRANSIENT)
- AudioManager.AUDIOFOCUS_GAIN 这个好像是长期获得焦点推荐做法会让其他播放器停止stop playback(对应AUDIOFOCUS_LOSS)
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 推荐做法会让其他播放器降低音量 setVolume playback(对应AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK)
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE 和上面的AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK差不多…暂时不知道什么区别
之所以说推荐做法是因为代码都是要自己写的,也可以自定义.
示例代码:
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; } }
6.处理AUDIO_BECOMING_NOISY
就是耳机的插口从洞里面拔出时,如果不做处理那就会让声音从手机扩音器放出来.顾名思义说的就是这种noisy的情况.
这时安卓会发送一个intent,我们需要定义一个广播去接受他.
manifest注册:
<receiver android:name=".MusicIntentReceiver"> <intent-filter> <action android:name="android.media.AUDIO_BECOMING_NOISY" /> intent-filter>receiver>
代码处理:
public class MusicIntentReceiver extends android.content.BroadcastReceiver { @Override public void onReceive(Context ctx, Intent intent) { if (intent.getAction().equals( android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) { // 暂停就可以了 } } }
7.播放本地音频
安卓系统会把音频信息加入数据库(找不到就重启下)方便我们通过Content Provider查找.
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());}
找到数据后进行调用:
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);
8.总结
以上是工作中经常用到的东西,安卓已经在APIGuide里帮我们总结了.我只是负责翻译,多看看官网的东西还是比较实用的,有事比去搜索一些博客要全面很多.
更多相关文章
- Android NDK c调用java代码
- Android 代码风格指南
- Android px和dip及sp的区别及转换代码
- Android 源代码编译前后的目录结构
- Android有用代码片段(三)
- Android 通过代码实现控制数据网络的开关(仅适用于5.0以上)
- 安卓手机恶意代码——Samsapo