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.状态处理

一句话,在错误的状态做错误的处理会出错.呵呵…..听起来像废话
先上一张图Android工作总结之如何做一个优秀的MediaPlayer音频播放器_第1张图片
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)