Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 如何制作音樂播放器 -- 播放音樂(上)

如何制作音樂播放器 -- 播放音樂(上)

編輯:關於Android編程

第7節 播放音樂

音樂播放列表也准備好了,我們來播放音樂吧。完成後效果如下,

\

實現音樂的播放,我們需要播放界面和音樂服務兩個方面的合作。

7.1 MusicService

前面我們已經為播放音樂的功能在MusicService中搭建好了框架,下面就要來實現它們了。這些實現接口的函數,我們在命名的時候都用上了xxxInner()這樣的格式,例如playInner(),這是功能真正實現的地方。

7.1.1 創建播放器

MediaPlayer是實現音樂播放的核心,所有與播放相關的操作都是通過它來完成的。當MusicService運行起來的時候,就需要創建它的實例,退出時,要釋放MediaPlayer實例,

public class MusicService extends Service {
    ......
    private MediaPlayer mMusicPlayer;

    @Override
    public void onCreate() {
        super.onCreate();
        ......
        //創建
        mMusicPlayer = new MediaPlayer();
        ......
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        ......
        //釋放
        mMusicPlayer.release();
    }
    ......
}

7.1.2 監聽器

MusicService提供注冊監聽器的接口,當播放音樂的狀態發生變化的時候,能夠實時的將當前的狀態傳遞給關心的組件。

監聽器的接口在之前的章節中,我們已經定義成了,

public class MusicService extends Service {

    public interface OnStateChangeListenr {

       void onPlayProgressChange(MusicItem item);
       void onPlay(MusicItem item);
       void onPause(MusicItem item);
    }
}

當某個組件(例如MusicListActivity)想要監聽MusicServcie的時候,就要利用registerOnStateChangeListener()和unregisterOnStateChangeListener()進行注冊。MusicService把注冊的監聽器存儲到數組列表當中,需要的時候取出來使用,

public class MusicService extends Service {

    //創建存儲監聽器的列表
    private List mListenerList = new ArrayList();

    private void registerOnStateChangeListenerInner(OnStateChangeListenr l) {
        //將監聽器添加到列表
        mListenerList.add(l);
    }

    private void unregisterOnStateChangeListenerInner(OnStateChangeListenr l) {
        //將監聽器從列表中移除
        mListenerList.remove(l);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        ......
        //當MusicService銷毀的時候,清空監聽器列表
        mListenerList.clear();
    }
}

7.1.3 控制播放

播放、暫停、播放前一首、播放後一首的功能實現,並通過監聽器通知監聽者MusicService的狀態變化。當前要播放的歌曲,將被放在叫做mCurrentMusicItem的MusicItem數據結構中,

public class MusicService extends Service {

    //當前是否為播放暫停狀態
    private boolean mPaused;
    //存放當前要播放的音樂
    private MusicItem mCurrentMusicItem;
    ......
    @Override
    public void onCreate() {
        super.onCreate();
        ......
        mPaused = false;
        ......
    }
    ......
    //將要播放的音樂載入MediaPlayer,但是並不播放
    private void prepareToPlay(MusicItem item) {
        try {
            //重置播放器狀態
            mMusicPlayer.reset();
            //設置播放音樂的地址
            mMusicPlayer.setDataSource(MusicService.this, item.songUri);
            //准備播放音樂
            mMusicPlayer.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //播放音樂,根據reload標志位判斷是非需要重新加載音樂
    private void playMusicItem(MusicItem item, boolean reload) {
        //如果這裡傳入的是空值,就什麼也不做
        if(item == null) {
            return;
        }

        if(reload) {
            //需要重新加載音樂
            prepareToPlay(item);
        }
        //開始播放,如果之前只是暫停播放,那麼音樂將繼續播放
        mMusicPlayer.start();
        //將音樂設置到指定時間開始播放,時間單位為毫秒
        seekToInner((int)item.playedTime);
        //將播放的狀態通過監聽器通知給監聽者
        for(OnStateChangeListenr l : mListenerList) {
            l.onPlay(item);
        }
        //設置為非暫停播放狀態
        mPaused = false;
    }

    //播放播放列表中,當前音樂的下一首音樂
    private void playNextInner() {
        int currentIndex = mPlayList.indexOf(mCurrentMusicItem);
        if(currentIndex < mPlayList.size() -1 ) {
            //獲取當前播放(或者被加載)音樂的下一首音樂
            //如果後面有要播放的音樂,把那首音樂設置成要播放的音樂
            //並重新加載該音樂,開始播放
            mCurrentMusicItem = mPlayList.get(currentIndex + 1);
            playMusicItem(mCurrentMusicItem, true);
        }
    }

    private void playInner() {
        //如果之前沒有選定要播放的音樂,就選列表中的第一首音樂開始播放
        if(mCurrentMusicItem == null && mPlayList.size() > 0) {
            mCurrentMusicItem = mPlayList.get(0);
        }

        //如果是從暫停狀態恢復播放音樂,那麼不需要重新加載音樂;
        //如果是從完全沒有播放過的狀態開始播放音樂,那麼就需要重新加載音樂
        if(mPaused) {
            playMusicItem(mCurrentMusicItem, false);
        } 
        else {
            playMusicItem(mCurrentMusicItem, true);
        }
    }

    private void playPreInner() {
        int currentIndex = mPlayList.indexOf(mCurrentMusicItem);
        if(currentIndex - 1 >= 0 ) {
            //獲取當前播放(或者被加載)音樂的上一首音樂
            //如果前面有要播放的音樂,把那首音樂設置成要播放的音樂
            //並重新加載該音樂,開始播放
            mCurrentMusicItem = mPlayList.get(currentIndex - 1);
            playMusicItem(mCurrentMusicItem, true);
        }
    }

    private void pauseInner() {
        //暫停當前正在播放的音樂
        mMusicPlayer.pause();
        //將播放狀態的改變通知給監聽者
        for(OnStateChangeListenr l : mListenerList) {
            l.onPause(mCurrentMusicItem);
        }
        //設置為暫停播放狀態
        mPaused = true;
    }

    private void seekToInner(int pos) {
        //將音樂拖動到指定的時間
        mMusicPlayer.seekTo(pos);
    }

    private MusicItem getCurrentMusicInner() {
        //返回當前正加載好的音樂
        return mCurrentMusicItem;
    }

    private boolean isPlayingInner() {
        //返回當前的播放器是非正在播放音樂
        return mMusicPlayer.isPlaying();
    }
    ......
}

7.1.4 通知播放進度

除了控制播放,還要能向監聽者報告當前音樂的播放進度。

我們設計的策略是:當音樂開始播放的時候,就每隔一秒鐘報告一次當前播放的進度。要實現每隔一秒報告一次,可以通過創建Handler,每間隔一秒發送一條通知的消息來實現。

public class MusicService extends Service {
    ......
    //定義循環發送的消息
    private final int MSG_PROGRESS_UPDATE = 0;

    //定義處理消息的Handler
    private Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_PROGRESS_UPDATE: {
                    //將音樂的時長和當前播放的進度保存到MusicItem數據結構中,
                    mCurrentMusicItem.playedTime = mMusicPlayer.getCurrentPosition();
                    mCurrentMusicItem.duration = mMusicPlayer.getDuration();

                    //通知監聽者當前的播放進度
                    for(OnStateChangeListenr l : mListenerList) {
                        l.onPlayProgressChange(mCurrentMusicItem);
                    }

                    //將當前的播放進度保存到數據庫中
                    updateMusicItem(mCurrentMusicItem);

                    //間隔一秒發送一次更新播放進度的消息
                    sendEmptyMessageDelayed(MSG_PROGRESS_UPDATE, 1000);
                }
                break;
            }
        }
    };

    //將播放時間更新到ContentProvider中
    private void updateMusicItem(MusicItem item) {

        ContentValues cv = new ContentValues();
        cv.put(DBHelper.DURATION, item.duration);
        cv.put(DBHelper.LAST_PLAY_TIME, item.playedTime);

        String strUri = item.songUri.toString();
        mResolver.update(PlayListContentProvider.CONTENT_SONGS_URI, cv, DBHelper.SONG_URI + "=\"" + strUri + "\"", null);
    }
    ......
}

當音樂開始播放的時候,觸發進度更新;當音樂暫停或者停止的時候,停止進度的更新;

public class MusicService extends Service {

   ......
   private void playMusicItem(MusicItem item, boolean reload) {
        ......
        //移除現有的更新消息,重新啟動更新
        mHandler.removeMessages(MSG_PROGRESS_UPDATE);
        mHandler.sendEmptyMessage(MSG_PROGRESS_UPDATE);
        ......
    }

    private void pauseInner() {
        ......
        //停止更新
        mHandler.removeMessages(MSG_PROGRESS_UPDATE);
        ......
    }

   @Override
    public void onDestroy() {
        super.onDestroy();
        ......
        //停止更新
        mHandler.removeMessages(MSG_PROGRESS_UPDATE);
        ......
   }
}

7.1.5 連續播放

我們設計的播放規則是-將播放列表裡的音樂按照次序自動播放,直到播完。所以我們要在每一首音樂結束後,自動播放下一首音樂。為此,要給播放器注冊一個播放是否完成的監聽器,當監聽器被觸發,開始下一首音樂的播放,

public class MusicService extends Service {
    ......
    @Override
    public void onCreate() {
        super.onCreate();
        ......
        mMusicPlayer = new MediaPlayer();
        mMusicPlayer.setOnCompletionListener(mOnCompletionListener);
        ......
    }

    private MediaPlayer.OnCompletionListener mOnCompletionListener = new MediaPlayer.OnCompletionListener() {

        @Override
        public void onCompletion(MediaPlayer mp) {
            //將當前播放的音樂記錄時間重置為0,更新到數據庫
            //下次播放就可以從頭開始
            mCurrentMusicItem.playedTime = 0;
            updateMusicItem(mCurrentMusicItem);
            //播放下一首音樂
            playNextInner();
        }
    };
}

7.1.6 初始化待播音樂

MusicService啟動之後,要先讀取數據庫中存儲的播放列表,把第一首音樂作為默認的待播放的音樂,

public class MusicService extends Service {

    private void initPlayList() {
        ......
        if( mPlayList.size() > 0) {
            mCurrentMusicItem = mPlayList.get(0);
        }
    }
}

7.1.7 添加到播放列表

關於我們定制的規則是這樣的:如果用戶選擇添加一首音樂,那麼添加後,自動播放該首音樂;如果用戶選擇添加一組音樂,那麼添加後,自動播放該組音樂列表中的第一首。所以添加播放列表的代碼還要做一些修改,

public class MusicService extends Service {
    ......
    private void addPlayListInner(List items) {

        mResolver.delete(PlayListContentProvider.CONTENT_SONGS_URI, null, null);
        mPlayList.clear();

        for (MusicItem item : items) {
            //逐個添加到播放列表,不要求添加後播放
            addPlayListInner(item, false);        
        }

        //添加完成後,開始播放
        mCurrentMusicItem = mPlayList.get(0);
        playInner();
    }

    //將函數增加了needPlay參數
    private void addPlayListInner(MusicItem item, boolean needPlay) {

        if(mPlayList.contains(item)) {
            return;
        }

        mPlayList.add(0, item);

        insertMusicItemToContentProvider(item);

        if(needPlay) {
            //添加完成後,開始播放
            mCurrentMusicItem = mPlayList.get(0);
            playInner();
        }
    }
    ......
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved