Android音频的播放

来源:互联网 发布:批量注册淘宝号 编辑:程序博客网 时间:2024/05/20 11:20

一、简介

Android的多媒体框架包括支持播放多种常见的媒体类型,使您可以轻松的把音频、视屏和图像集成到您的应用。您可以播放音频或视屏媒体文件,这些文件是存储在你的应用程序的资源文件中的。应用程序的资源文件可以是文件系统中独立的文件,或者通过网络连接获取的一个数据流,所有使用MediaPlayer APIS的资源文件。

注意:您只能在标准输出设备上播放音频数据。目前,标准输出设备是移动设备的扬声器或耳机。您不能在谈话音频调用期间播放声音文件。

1、基础
下面的类是Android框架中用于播放声音的类:
MediaPlayer:这个类主要用于播放声音和视屏
AudioManager:这个类主要管理音频和音频输出设备

2、声明Manifest文件
//网络权限申明

<uses-permission android:name="android.permission.INTERNET" />

如果您的播放应用需要阻止屏幕变暗或者阻止处理器睡眠,或者使用MediaPlayer.setScreenOnWhielePlaying()或MedidaPlayer.setWakeMode()方法,需要加入下面的权限。
//唤醒锁申明

<uses-permission android:name="android.permission.WAKE_LOCK" />

二、使用MediaPlayer

媒体框架中最重要的组件之一就是MediaPlayer类,此类的对象可以用少量的设置即能获取,解码和播放音频视屏,它支持多种媒体源,比如:

  • 本地资源
  • 内部URI,比如从您的ContentResolver取得的URI
  • 外部URI(媒体流)

1、播放本地资源

//播放本地资源文件,create方法中包括了setDataSource和prepare的执行MediaPlayer _mp = MediaPlayer.create(this, R.raw.millets);_mp.start();

2、播放一个本地URI

//播放文本文件的资源文件                String _path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES) + "/millets.mp4";                try {                    //也可以采用这种方式创建对象                    MediaPlayer _mpFile = new MediaPlayer();                    _mpFile.setDataSource(this, Uri.parse(_path));                    _mpFile.prepare();//同步执行                    _mpFile.start();                } catch (IOException e) {                    e.printStackTrace();                }

3、播放网络地址

//播放网络的资源                String _netPath = "http://.mp4";                MediaPlayer _netMp = new MediaPlayer();                try {                    _netMp.setDataSource(this, Uri.parse(_netPath));                    _netMp.prepareAsync();//异步执行                    //需要等待prepareAsync准备结束后,回调通知才可以播放                    _netMp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {                        @Override                        public void onPrepared(MediaPlayer mp) {                            mp.start();                        }                    });                } catch (IOException e) {                    e.printStackTrace();                }

4、管理状态
MediaPlayer有一个内部的状态,因为特定的操作只能在特定的状态时才能有效。如果你在错误的状态下执行一个操作,系统可能抛出一个异常或者导致一个意外的行为。
这里写图片描述

5、释放MediaPlayer
MediaPlayer可能要消耗大量的系统资源。因此您应该总是采用一些额外的措施来确保在一个MediaPlayer实例上不会挂起太长的时间。当您用玩MediaPlayer时,您应该总是调用release()来保证任何分配给MeidaPlayer的系统资源被正确的释放。

mediaPlayer.release();mediaPlayer = null;

6、完整例子:

public class MediaMainActivity extends BaseActivity implements View.OnClickListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {    //UI    private android.widget.Button buttonpre;    private android.widget.Button buttonstart;    private android.widget.Button buttonpause;    private android.widget.Button buttonnext;    private MediaPlayer mMediaPlayer;    //data    private ArrayList<String> mMusicList;    private int mMusicIndex = 0;    private boolean isPaused = false;    @Override    public void initData(Bundle savedInstanceState) {        //init music data        String _musicPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getPath();        String _a = _musicPath + File.separator + "mbc.mp3";        String _b = _musicPath + File.separator + "rgyzd.mp3";        String _c = _musicPath + File.separator + "rga.mp3";        String _d = _musicPath + File.separator + "zxk.mp3";        String _e = _musicPath + File.separator + "aj.mp3";        mMusicList = new ArrayList<>();        mMusicList.add(_a);        mMusicList.add(_b);        mMusicList.add(_c);        mMusicList.add(_d);        mMusicList.add(_e);        //init media        mMediaPlayer = new MediaPlayer();        mMediaPlayer.setOnPreparedListener(this);        mMediaPlayer.setOnCompletionListener(this);        mMediaPlayer.setOnErrorListener(this);    }    @Override    public void initView(Bundle savedInstanceState) {        setContentView(R.layout.activity_media_main);        this.buttonnext = (Button) findViewById(R.id.button_next);        this.buttonnext.setOnClickListener(this);        this.buttonpause = (Button) findViewById(R.id.button_pause);        this.buttonpause.setOnClickListener(this);        this.buttonstart = (Button) findViewById(R.id.button_start);        this.buttonstart.setOnClickListener(this);        this.buttonpre = (Button) findViewById(R.id.button_pre);        this.buttonpre.setOnClickListener(this);    }    @Override    public void loadData(Bundle savedInstanceState) {    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.button_start:                if (isPaused) {                    mMediaPlayer.start();                    isPaused = false;                } else {                    this.startPlayMusic();                }                break;            case R.id.button_pause:                pausePlay();                break;            case R.id.button_pre:                this.startPre();                break;            case R.id.button_next:                this.startNext();                break;        }    }    /**     * 开始重新播放音乐     */    private void startPlayMusic() {        if (mMediaPlayer.isPlaying()) {            mMediaPlayer.stop();        }        mMediaPlayer.reset();        try {            mMediaPlayer.setDataSource(this, Uri.parse(mMusicList.get(mMusicIndex)));            mMediaPlayer.prepareAsync();            isPaused = false;        } catch (IOException e) {            e.printStackTrace();        }    }    /**     * 暂停播放     */    private void pausePlay() {        if (mMediaPlayer.isPlaying()) {            mMediaPlayer.pause();            isPaused = true;        }    }    /**     * 播放上一首     */    private void startPre() {        if (mMusicIndex - 1 >= 0) {            mMusicIndex--;        } else {            mMusicIndex = mMusicList.size() - 1;        }        this.startPlayMusic();    }    /**     * 播放下一首     */    private void startNext() {        if (mMusicIndex + 1 < mMusicList.size()) {            mMusicIndex++;        } else {            mMusicIndex = 0;        }        this.startPlayMusic();    }    @Override    public void onCompletion(MediaPlayer mp) {        this.startNext();    }    @Override    public boolean onError(MediaPlayer mp, int what, int extra) {        if (null != mMediaPlayer) {            mMediaPlayer.reset();        }        return true;    }    @Override    public void onPrepared(MediaPlayer mp) {        mp.start();    }    @Override    protected void onDestroy() {        super.onDestroy();        if (null != mMediaPlayer) {            if (mMediaPlayer.isPlaying()) {                mMediaPlayer.stop();            }            mMediaPlayer.release();        }    }}

三、在服务中使用MediaPlayer

如果您希望您的媒体在您的应用不出现在屏幕上时仍然能够在后台播放,也就是您希望当用户与其他应用交互时仍能继续播放,那么您必须启动一个Service并且通过它来控制MediaPlayer实例。
1、开启服务

public class MyService extends Service implements MediaPlayer.OnPreparedListener {    private static final String 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();    }}

2、处理异步的错误:

public class MyService extends Service implements MediaPlayer.OnErrorListener {    MediaPlayer mMediaPlayer;    public void initMediaPlayer() {        // ...initialize the MediaPlayer here...        mMediaPlayer.setOnErrorListener(this);    }    @Override    public boolean onError(MediaPlayer mp, int what, int extra) {        // ... react appropriately ...        // The MediaPlayer has moved to the Error state, must be reset!    }}

3、使用唤醒锁
在后台播放媒体的应用时,当您的Service正在运行时,设备可能进入休眠,因为Android系统在休眠时会试着节省电能,那么系统会试着关闭电话的任何不必要的特性,包括CPU和WIFI,然而,如果您的Service正在播放或者接受音乐,您就想阻止系统干涉您的播放工作,为了在上述情况下保证您的service继续运行,您就必须使用”wakelocks”。一个wakelock是一种通知系统在手机空闲时也能为您的应用保留所用特性的途径。
注意:您总是应该保守的使用wekelocks并且尽在真正需要时才持有它,因为它会显著减少设备电池的寿命。
①设置唤醒锁:
当您的MediaPlayer播放时,要保持CPU持续运行,在初始化MediaPlayer时需要调用setWakeMode(),一旦您这样做了,MediaPlayer就会在播放时持有一个特定的锁,并在暂停或停止时释放它:

mMediaPlayer = new MediaPlayer();// ... other initialization here ...mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

4、请求WIFILOCK:

WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))    .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");wifiLock.acquire();

①释放锁
当您暂停或者停止媒体或者当你现在不需要网络时,您应该释放这个锁:

wifiLock.release();

5、使用Notification来在前台显示

String songName;// assign the song name to songNamePendingIntent 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);

6、完整代码:
①Activity

public class MediaPlayServiceActivity extends BaseActivity implements View.OnClickListener {    private android.widget.Button servicebuttonstart;    private android.widget.Button servicebuttonpause;    private android.widget.Button servicebuttonover;    @Override    public void initData(Bundle savedInstanceState) {    }    @Override    public void initView(Bundle savedInstanceState) {        setContentView(R.layout.activity_media_play_service);        this.servicebuttonover = (Button) findViewById(R.id.service_button_over);        this.servicebuttonover.setOnClickListener(this);        this.servicebuttonpause = (Button) findViewById(R.id.service_button_pause);        this.servicebuttonpause.setOnClickListener(this);        this.servicebuttonstart = (Button) findViewById(R.id.service_button_start);        this.servicebuttonstart.setOnClickListener(this);    }    @Override    public void loadData(Bundle savedInstanceState) {    }    @Override    public void onClick(View v) {        switch (v.getId()) {            //启动播放音乐服务            case R.id.service_button_start:                Intent _intentStart = new Intent(this, MediaPlayService.class);                _intentStart.setAction(Common.ACTION_START);                startService(_intentStart);                break;            //启动暂停音乐服务            case R.id.service_button_pause:                Intent _intentPause = new Intent(this, MediaPlayService.class);                _intentPause.setAction(Common.ACTION_PAUSE);                startService(_intentPause);                break;            //启动停止音乐服务            case R.id.service_button_over:                Intent _intentOver = new Intent(this, MediaPlayService.class);                _intentOver.setAction(Common.ACTION_OVER);                startService(_intentOver);                break;        }    }    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)    @Override    public boolean onKeyDown(int keyCode, KeyEvent event) {        if (keyCode == KeyEvent.KEYCODE_BACK) {        }        return super.onKeyDown(keyCode, event);    }}

②Service

public class MediaPlayService extends Service implements MediaPlayer.OnPreparedListener, AudioManager.OnAudioFocusChangeListener {    //声明MediaPlayer    private MediaPlayer mMP;    //声明音乐的地址    private String mMusicPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC) + File.separator + "zxk.mp3";    //声明WifiLock    private WifiManager.WifiLock mWifiLock;    //声明AudioManager    private AudioManager mAudioManager;    @Override    public void onCreate() {        super.onCreate();        //获取音频对象        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);        int _results = mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);        if (AUDIOFOCUS_REQUEST_GRANTED == _results) {            initMediaPlayer();        } else {        }    }    /**     * 初始化MediaPlayer中的一些配置     */    public void initMediaPlayer() {        mMP = new MediaPlayer();        mMP.setOnPreparedListener(this);        //保证不会被后台清理掉        mMP.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);        //保证WIFI不会被休眠        WifiManager _wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);        mWifiLock = _wifiManager.createWifiLock("MyLock");        mWifiLock.acquire();        //设置Notification        createNotification();    }    /**     * 创建一个通知,用于在前台显示     */    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)    private void createNotification() {        Notification.Builder _builder = new Notification.Builder(this);        _builder.setTicker("我的第一个音乐播放器");        _builder.setSmallIcon(R.mipmap.ic_launcher);        _builder.setContentTitle("我的音乐神器");        _builder.setContentInfo("正在播放");        PendingIntent _pI = PendingIntent.getActivity(this, 0, new Intent(this, MediaPlayServiceActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);        _builder.setContentIntent(_pI);        NotificationManager _nM = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);        Notification _notification = _builder.build();        _nM.notify(0, _notification);        startForeground(0, _notification);    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        String _stringAction = intent.getAction();        switch (_stringAction) {            //播放音乐action            case Common.ACTION_START:                mMP.reset();                try {                    mMP.setDataSource(getApplicationContext(), Uri.parse(mMusicPath));                    mMP.prepareAsync();                } catch (IOException e) {                    e.printStackTrace();                }                break;            //暂停音乐action            case Common.ACTION_PAUSE:                if (mMP.isPlaying()) {                    mMP.pause();                }                break;            //结束音乐action            case Common.ACTION_OVER:                if (mMP.isPlaying()) {                    mMP.stop();                }                mMP.release();                break;            case Common.ACTION_STOP:                if (mMP.isPlaying()) {                    mMP.stop();                }                break;        }        return super.onStartCommand(intent, flags, startId);    }    @Override    public IBinder onBind(Intent intent) {        // TODO: Return the communication channel to the service.        throw new UnsupportedOperationException("Not yet implemented");    }    @Override    public void onPrepared(MediaPlayer mp) {        mp.start();    }    @Override    public void onDestroy() {        super.onDestroy();        //释放WifiLock        mWifiLock.release();        //停止通知在前台显示        stopForeground(true);        //解绑监听        if (null != this.mAudioManager) {            this.mAudioManager.abandonAudioFocus(this);        }    }    @Override    public void onAudioFocusChange(int focusChange) {        switch (focusChange) {            //已经获得焦点            case AudioManager.AUDIOFOCUS_GAIN:                if (null == mMP) {                    this.initMediaPlayer();                } else if (!mMP.isPlaying()) {                    mMP.start();                }                mMP.setVolume(1.0f, 1.0f);                break;            //长期失去焦点            case AudioManager.AUDIOFOCUS_LOSS:                if (null != mMP) {                    if (mMP.isPlaying()) {                        mMP.stop();                    }                    mMP.release();                    mMP = null;                }                break;            //失去焦点,但是很快将会获取            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:                if (null != mMP) {                    if (mMP.isPlaying()) {                        mMP.pause();                    }                }                break;            //失去焦点,允许以小声音播放            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:                if (null != mMP) {                    if (mMP.isPlaying()) {                        mMP.setVolume(0.1f, 0.1f);                    }                }                break;        }    }}

四、处理音频的焦点

1、要请求音频焦点,您必须从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()方法在音频焦点发生改变时调用,因此,您也可以在您的Service和Activity上实现此接口
2、Service实现onAudioFocusChangeListener接口

class MyService extends Service                implements AudioManager.OnAudioFocusChangeListener {    // ....    public void onAudioFocusChange(int focusChange) {        // Do something based on focus change...    }}

注意:参数focusChange告诉你焦点如何发生了变化

  • AUDIOFOCUS_GAIN: You have gained the audio focus.
  • AUDIOFOCUS_LOSS: You have lost the audio focus for a presumably long time. You must stop all audio playback. Because you should expect not to have focus back for a long time, this would be a good place to clean up your resources as much as possible. For example, you should release the MediaPlayer.
  • AUDIOFOCUS_LOSS_TRANSIENT: You have temporarily lost audio focus, but should receive it back shortly. You must stop all audio playback, but you can keep your resources because you will probably get focus back shortly.
  • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: You have temporarily lost audio focus, but you are allowed to continue to play audio quietly (at a low volume) instead of killing audio completely.

注意:音频焦点API仅在API level8(Android2.2)及更高版本上可以使用

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;    }}

3、完整代码见上

五、处理AUDIO_BECOMING_NOISY意图(耳机拔出)

很多良好的音频播放的应用都会在那些导致声音变成噪音(通过外部扬声器输出)的事件发生时自动停止播放。例如,这可能发生在当一个用户用耳机听音乐时突然断开了耳机连接,音频从扬声器播放可能不是用户所期望的。
1、可以通过处理ACTION_AUDIO_BECOMIING_NOISY intent来保证您的应用在此情况下停止播放音乐,您可以把如下代码添加到您的manifest来注册一个receiver。

<receiver android:name=".MusicIntentReceiver">   <intent-filter>      <action android:name="android.media.AUDIO_BECOMING_NOISY" />   </intent-filter></receiver>

2、创建一个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)) {          // signal your service to stop playback          // (via an Intent, for instance)      }   }}
0 0