Android歌詞秀設計思路(3)通用的音樂播放服務(上) – Android移動開發技術文章_手機開發 Android移動開發教學課程

MediaPlayerService作為通用的音樂播放Service類,它的功能有:
控制音樂播放,停止,暫停,前/後歌曲切換。
Audio Focus相關處理(對應應用程序切換)。
Intent處理(對應多媒體鍵,耳機線拔出,打入電話)
Notification處理
其實這個類本來是和LyricPlayerService在一起的,但是隨著功能的增加代碼越來越亂。於是就有瞭分開的想法。但是分也要分出點名堂,於是為這個類定義的功能就是實現一個通用的媒體播放服務。
分開後的類關系可以從下面的全體圖中看出。

下面針對各功能加以說明
控制音樂播放,停止,暫停,前/後歌曲切換。
在開始真正的工作之前要解決的一個問題是:作為通用的音樂播放那個服務,就一定會還有在後臺時播放前/後一首歌的要求。但是這是應用已經不存在瞭,不可能把這個任務交個Activie來處理。為瞭解決這個問題定義瞭一個用於取得相應的媒體文件信息的MediaInfoProvider類。
public interface MediaInfoProvider{ 
    String getUrl(); 
    String getTitle(); 
    boolean moveToNext(); 
    boolean moveToPrev(); 
}
通過調用moveToNext和moveToPrev方法實現媒體文件的切換,然後通過調用getUrl和getTitle來取得文件的信息。
註意在這裡隻是規定接口,真正的實例必須由利用者通過下面的方法來提供。有一點額外的工作就是如果希望播放的歌曲和正在播放的不一樣的話就先停止正在播放的歌曲。
public void setMediaInfoProvider(MediaInfoProvider provider) {   
    if( mDataSource != provider.getUrl() && (isStop() == false)){ 
        stop(); 
    } 
    mMediaInfoProvider = provider; 
    mDataSource = provider.getUrl(); 
    mTitle = provider.getTitle(); 

當然在利用MediaPlayer之前我們需要onCreate方法中進行一些準備工作。內容包括
創建MediaPlayer對象,
設定喚醒方式(目的是防止播放中CPU進入休眠狀態,記得在 AndroidManifest.xml中設定權限為android.permission.WAKE_LOCK)
指定播放完瞭後的監聽器。
mMediaPlayer = new MediaPlayer(); 
mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); 
mMediaPlayer.setOnCompletionListener(this);
 
這個播放完瞭監聽器的目的有兩個。一個是停止任務欄上的圖標顯示,另外一個是在沒有前臺應用的時候自動關閉本服務。要是沒有人管瞭怎麼辦?對吧。
@Override
    public void onCompletion(MediaPlayer mp) { 
        mIsPausing = false; 
        mNotificationManager.cancel(NOTIFICATION); 
        if(mBinded == false){ 
            stopSelf(); 
        } 
    }  
然後就是onDestroy中進行適當的收尾工作。其中停止播放的部分看起來好像沒有也可以,還是一切都按部就班好一些吧。
mMediaPlayer.setOnCompletionListener(null); 
if(!isStop()){ 
    stop(); 

mMediaPlayer.release(); 
mMediaPlayer = null;
 終於到到瞭真正的控制部分。接下來的程序比較長但內容確實比較簡單。
在最前面的start方法比較復雜,主要是因為雖然說起來都是播放,但是從暫停恢復播放和播放新歌的處理有所不同。其他的沒有什麼好講的。
public void start() { 
        tryToGetAudioFocus(); 
        if(!isPausing()){ 
            if(isPlaying()){ 
                stop(); 
            } 
            try{    
                mMediaPlayer.reset();    
                mMediaPlayer.setDataSource(mDataSource);    
                mMediaPlayer.prepare();  
            }catch(IOException e) {    
                return;    
            } catch  (IllegalArgumentException e) {    
                return;    
            }    
            mMediaPlayer.start(); 
        }else{ 
            mIsPausing = false; 
            mMediaPlayer.start(); 
        } 
        showNotification(); 
    }    
     
    public void stop() { 
        giveUpAudioFocus(); 
        mMediaPlayer.stop();   
        mIsPausing = false; 
        mNotificationManager.cancel(NOTIFICATION); 
    }    
     
    public void pause() { 
        giveUpAudioFocus(); 
        mIsPausing = true; 
        mMediaPlayer.pause(); 
    } 
     
    public void playNext(){ 
        if(mMediaInfoProvider.moveToNext()){ 
            if(isPlaying() || isPausing()){ 
                stop(); 
            } 
            mDataSource = mMediaInfoProvider.getUrl(); 
            mTitle = mMediaInfoProvider.getTitle(); 
            start(); 
        } 
    } 
     
    public void playPrev(){ 
        if(mMediaInfoProvider.moveToPrev()){ 
            if(isPlaying() || isPausing()){ 
                stop(); 
            } 
            mDataSource = mMediaInfoProvider.getUrl(); 
            mTitle = mMediaInfoProvider.getTitle(); 
            start(); 
        } 
    } 
     
    public boolean isPausing(){ 
        return mIsPausing; 
    } 
     
    public boolean isPlaying() {    
        return mMediaPlayer.isPlaying();    
    }   
     
    public boolean isStop(){ 
        return (isPlaying() == false && isPausing() == false); 
    } 
     
    public int getDuration() {    
        return mMediaPlayer.getDuration();    
    }    
        
    public int getPosition() {    
        return mMediaPlayer.getCurrentPosition();    
    }    
     
    public long seek(long whereto) { 
        mMediaPlayer.seekTo((int ) whereto); 
        return whereto; 
    }
下面的代碼的功能是處理各種有關播放控制的Intent。媒體鍵(就是其實就是線控上的那幾個鈕),耳機線拔出,來電暫停都是通過這種方式實現的。當然它們都需要在AndroidManifest.xml中進行定義。
public static final String ACTION_PAUSE = "MediaPlayer.xwg.action.PAUSE"; 
public static final String ACTION_PLAY_PAUSE = "MediaPlayer.xwg.action.PLAY_PAUSE"; 
public static final String ACTION_PREVIOUS = "MediaPlayer.xwg.action.PREVIOUS"; 
public static final String ACTION_NEXT = "MediaPlayer.xwg.action.NEXT"; 
   @Override
public int onStartCommand(Intent intent, int flags, int startId) { 
    String action = intent.getAction(); 
    if(action != null) 
    { 
        if (action.equals(ACTION_PLAY_PAUSE)){ 
            if(isPlaying()){ 
                pause(); 
            }else{ 
                start(); 
            } 
        }else if (action.equals(ACTION_PAUSE)){ 
            if(isPlaying()) 
            { 
                pause(); 
            } 
        }else if (action.equals(ACTION_PREVIOUS)){ 
            if(isPlaying() || isPausing()) 
            { 
                playPrev(); 
            } 
        }else if (action.equals(ACTION_NEXT)){ 
            if(isPlaying() || isPausing()) 
            { 
                playNext(); 
            } 
        } 
    } 
       return START_NOT_STICKY; // Means we started the service, but don't want it to 
           // restart in case it's killed. 
}
其實在前面代碼中的有幾個地方沒有提,就是關於AudioFocus和MEDIA_BUTTOn,,耳機插頭拔出,來電處理和Notification表示的部分。關於這部分內容請參閱
Android歌詞秀設計思路(4)通用的音樂播放服務(下)
軟件功能說明:原創:Android應用開發-Andorid歌詞秀,含源碼
工程,源碼下載:Android歌詞秀源碼,工程文件2011/9/11版
 
 
作者“來自大連”

發佈留言