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里帮我们总结了.我只是负责翻译,多看看官网的东西还是比较实用的,有事比去搜索一些博客要全面很多.

更多相关文章

  1. Android对返回键进行处理的方式
  2. Android(安卓)Service
  3. Android(安卓)手势
  4. 探索 Android(安卓)平台的 CameraX
  5. Android开发之消息处理机制(二)——消息循环
  6. android中处理json最佳方法
  7. ViewPager实现QQ主界面,ViewFlipper实现某些新闻应用,自动播放,很
  8. Android(安卓)TextUtils类介绍
  9. Android(安卓)下的usb框架及功能点

随机推荐

  1. Android中的Handler, Looper, MessageQue
  2. 用户空间第一个程序Init
  3. android 背景平铺
  4. android ssl
  5. Android(安卓)SDK下, 如何在程序中输出日
  6. android 多线程处理UI
  7. Android(安卓)- Android(安卓)Studio修改
  8. android camera 源码分析(基于应用)
  9. 【Android】状态栏通知Notification、Not
  10. andio:android 音频的代码层次关系