天天看點

手把手教你做音樂播放器(四)播放服務的搭建第4節 播放服務的搭建

第4節 播放服務的搭建

播放音樂的服務-

MusicService

是整改音樂播放器的核心,它将播放界面和實際的播放功能連接配接在一起。

手把手教你做音樂播放器(四)播放服務的搭建第4節 播放服務的搭建

4.1 MusicService的接口

它對外要提供兩類主要的功能,播放音樂的控制接口和播放清單的存取接口。

4.1.1 播放音樂的控制接口

根據功能的定義,我們認為控制接口應該包括:

play():播放播放清單中應該要播放的音樂;
playPre():播放播放清單中的上一首音樂;
playNext():播放播放清單中的下一首音樂;
pause():暫停播放;
isPlaying():目前音樂是否處于播放的狀态;
seekTo():将目前音樂播放的進度,拖動到指定的位置;
getCurrentMusicItem():擷取目前正在播放的音樂的資訊;
           

MusicListActivity

為了擷取

MusicService

的狀态,還需要設定監聽器,當

MusicService

有變化時,能主動通知到

MusicListActivity

。是以要有監聽函數:

registerOnStateChangeListener():注冊監聽函數
unregisterOnStateChangeListener():登出監聽函數
           

監聽時要擷取的資訊包括:

  1. 播放進度的改變;
  2. 音樂開始播放;
  3. 音樂停止播放;

是以設計監聽器的接口為,

public interface OnStateChangeListenr {

    //用來通知播放進度
    void onPlayProgressChange(MusicItem item);
    //用來通知目前處于播放狀态
    void onPlay(MusicItem item);
    //用來通知目前處于暫停或停止狀态
    void onPause(MusicItem item);
}
           

當然,還可以設計擷取更多的資訊,這裡我們就簡單的擷取這幾種簡單的資訊。隻要知道了設計的原理,我們就可以在以後随心所欲的改造。

4.1.2. 播放清單的存取接口;

MusicService

通過操作

PlayListContentProvider

來實作對音樂清單的存取。

MusicService

對外提供這樣的接口:

getPlayList():擷取播放清單
addPlayList():添加播放清單。這裡添加清單應該有兩種形式,一種是一次性添加多首音樂,一種是一次就添加一首音樂。
           

4.2 Service的使用

安卓系統的

Service

按照建立的方式進行分類,有兩種:啟動Service-

start Service

,綁定Service-

bind Service

。前者使用

startService()

運作,後者使用

bindService()

運作。

4.2.1 Start Service

其他元件通過調用

startService()

函數将

Service

運作起來,再通過調用

stopService()

函數讓其停止運作。

單純的使用這種形式的

Service

最為簡單,它和它的調用者之間沒有什麼聯系,調用者隻是負責啟動它和停止它,或者在啟動它的時候通過Intent傳遞一點資料,除此之外,兩者沒有資料交換、沒有其他的功能調用,這兩個元件之間基本上互不影響。

手把手教你做音樂播放器(四)播放服務的搭建第4節 播放服務的搭建

設計這樣的一個

Service

需要,

  1. 繼承Android SDK提供的

    Service

    類,重寫

    onBind()

    函數,讓它傳回空值;
    public class MyService extends Service {
    
        public MyService() {
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            //不需要調用者和Service有功能調用,傳回空值
            return null;
        }
        ......
    }
               
  2. AndroidManifest.xml

    中,聲明新建立的

    Service

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
    
        ......
        <application
            ......
            android:theme="@style/AppTheme">
    
            ......
            <!--聲明新建立的Service-->
            <service
                android:name=".MyService"
                android:enabled="true"
                android:exported="true"></service>
        </application>
    
    </manifest>
               

使用這種

Service

也很簡單。假設Activity A中有個按鈕start,點選之後就調用

startService

;還有個按鈕B-stop,點選之後就調用

stopService

public void onClick(View v)
{

    switch (v.getId())
    {
        case R.id.start:
        {
            //啟動Service
            Intent i = new Intent(this, MyService.class);
            startService(i);
        }
        break;

        case R.id.stop:
        {
            //停止Service
            Intent i = new Intent(this, MyService.class);
            stopService(i);
        }
        break;
    }
}
           

這裡運作Service的時候,是通過

Intent

明确指定被運作的

Service

。這種明确指定啟動哪個

Service

的方式叫做

Service

的顯示調用。與之對應的還有隐式調用,通過

action

來啟動。

4.2.2 Bind Service

其他元件通過調用

bindService()

綁定

Service

,讓它運作起來;再通過調用

unbindService()

解除綁定。

這種形式的

Service

與調用者之間通常有功能調用或者頻繁的資料交換,

Service

會提供接口給其它子產品調用,

手把手教你做音樂播放器(四)播放服務的搭建第4節 播放服務的搭建

設計這樣的一個

Service

需要,

  1. 繼承Android SDK提供的

    Service

    類,
    public class MyService extends Service {
    
        public MyService() {
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            //暫時傳回空值,寫下來将進行改造
            return null;
        }
        ......
    }
               
  2. 實作一個自定義的

    Binder

    ,讓它這個繼承

    Binder

    類。

    Binder

    可以将

    Service

    與調用者聯系起來,在

    Binder

    中提供的方法,就是

    Service

    對外提供的方法。

    元件和

    Service

    之間的調用是通過

    Binder

    來進行的。我們可以把

    Binder

    看作是一個連接配接其他元件和

    Service

    的橋梁,它的實作原理是什麼,我們暫時不用去關心,隻要知道這樣用就可以了。
    public class MyService extends Service {
    
        ......
    
        //建立一個自定義的Binder
        public class MyServiceIBinder extends Binder {
            //提供給其他元件調用的方法
            public void function1(int param){
                //調用Service中真正實作功能的方法
                innerFunction1(param);
            }
        }
    
        //真正實作功能的方法
        private void innerFunction1(int param) {
    
        }
    
        //建立Binder執行個體
        private final IBinder mBinder = new MyServiceIBinder();
    
        @Override
        public IBinder onBind(Intent intent) {
            //當元件bindService()之後,将這個Binder傳回給元件使用
            return mBinder;
        }
    
        ......
    }
               
  3. AndroidManifest.xml

    中,聲明新建立的

    Service

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
    
        ......
        <application
            ......
            android:theme="@style/AppTheme">
    
            ......
            <!--聲明新建立的Service-->
            <service
                android:name=".MyService"
                android:enabled="true"
                android:exported="true"></service>
        </application>
    
    </manifest>
               

其他元件使用這個

Service

的時候,

  1. 建立一個

    ServiceConnection

    ,當綁定

    Service

    之後,在

    onServiceConnected()

    中會得到

    Service

    傳回的

    Binder

    ;如果Service遇到異常情況退出時,會通過

    onServiceDisconnected()

    通知綁定它的元件。
    private ServiceConnection mServiceConnection = new ServiceConnection()
    {
    
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //這裡的service參數,就是Service當中onBind()傳回的Binder
            //擷取通路Service的橋梁-MyServiceIBinder
            MyService.MyServiceIBinder bridge = (MyService.MyServiceIBinder) service;
    
            //通過橋梁就可以調用到Service提供到函數了
            bridge.function1();
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
            //當Service遇到異常情況退出時,會通過這裡通知綁定過它的元件
        }
    };
               
    獲得了

    MyService.MyServiceIBinder

    之後,我們就可以向調用普通函數那樣,調用到

    Service

    對外提供的接口函數了。

    需要注意的是,如果使用者主動解除綁定,

    onServiceDisconnected()

    是不會被觸發的。
  2. 假設Activity A中有個按鈕,點選之後就調用

    bindService

    ;還有個按鈕B,點選之後就調用

    unbindService

    public void onClick(View v)
    {
    
        switch (v.getId())
        {
            case R.id.start:
            {
                Intent i = new Intent(this, MyService.class);
                bindService(i, mServiceConnection, Context.BIND_AUTO_CREATE);
            }
            break;
    
            case R.id.stop:
            {
                unbindService(mServiceConnection);
            }
            break;
        }
    }
               
    這裡同樣采用的是對

    Service

    的顯示調用。

4.2.3 混合模式

Service

并不是隻能給一個元件使用,它可以同時服務于多個元件。

是以一個

Service

既可以是

Start Service

,也可以是

Bind Service

。隻要把兩者需要實作的地方都實作了就行。元件A可以通過

startService()

運作一個

Service

,元件B可以通過

bindService()

再次運作同一個

Service

手把手教你做音樂播放器(四)播放服務的搭建第4節 播放服務的搭建

4.3 MusicService的建立

根據上面的介紹可以知道,因為

MusicService

要為其它元件提供調用的接口,是以它至少是一個

Bind Service

MusicListActivity

通過

MusicService

提供的

IBinder

對象,使用

bindService()

方式與之建立聯系,擷取播放音樂、暫停音樂等等一系列與播放音樂有關的操作方法;

是以

MusicService

應該按照如下方式設計,

public class MusicService extends Service {

        ......

        public class MusicServiceIBinder extends Binder {

            public void addPlayList(List<MusicItem> items) {
                addPlayListInner(items);
            }

            public void addPlayList(MusicItem item) {
                addPlayListInner(item);
            }

            public void play() {
                playInner();

            }

            public void playNext() {
                playNextInner();

            }

            public void playPre() {
                playPreInner();
            }

            public void pause() {
                pauseInner();
            }

            public void seekTo(int pos) {
                seekToInner(pos);
            }

            public void registerOnStateChangeListener(OnStateChangeListenr l) {
                registerOnStateChangeListenerInner(l);

            }

            public void unregisterOnStateChangeListener(OnStateChangeListenr l) {
                unregisterOnStateChangeListenerInner(l);
            }

            public MusicItem getCurrentMusic() {
                return getCurrentMusicInner();
            }

            public boolean isPlaying() {
                return isPlayingInner();
            }

            public List<MusicItem> getPlayList() {
                return null;
            }

        }

        //真正實作功能的方法
        public void addPlayListInner(List<MusicItem> items) {

        }

        public void addPlayListInner(MusicItem item) {

        }

        public void playNextInner() {
              }

        public void playInner() {

        }

        public void playPreInner() {
        }

        public void pauseInner() {

        }

        public void seekToInner(int pos) {

        }

        public void registerOnStateChangeListenerInner(OnStateChangeListenr l) {

        }

        public void unregisterOnStateChangeListenerInner(OnStateChangeListenr l) {

        }

        public MusicItem getCurrentMusicInner() {
           return null;
        }

        public boolean isPlayingInner() {
           return false;
        }

        //建立Binder執行個體
        private final IBinder mBinder = new MusicServiceIBinder();

        @Override
        public IBinder onBind(Intent intent) {
            //當元件bindService()之後,将這個Binder傳回給元件使用
            return mBinder;
        }

        ......
}
           

4.4 MusicService的使用

MusicListActivity

綁定

MusicService

的時候,就先定義一個

ServiceConnection

private MusicService.MusicServiceIBinder mMusicService;

private ServiceConnection mServiceConnection = new ServiceConnection() {

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {

        //綁定成功後,取得MusicSercice提供的接口
        mMusicService = (MusicService.MusicServiceIBinder) service;

    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
};
           

然後在

onCreate()

當中,實作綁定的操作,

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_music_list);

   Intent i = new Intent(this, MusicService.class);
   //啟動MusicService
   startService(i);
   //實作綁定操作
   bindService(i, mServiceConnection, BIND_AUTO_CREATE);
}
           

MusicListActivity

退出的時候,需要将

MusicService

解除綁定,

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

   ......
   unbindService(mServiceConnection);
   ......
}
           

這裡需要注意一個細節:在綁定

MusicService

之前,我們先做了一次

startService()

。假如不先

startService()

,那麼當

MusicListActivity

退出(onDestroy())的時候,

MusicService

将會被銷毀掉(因為調用了unbindService())。

如果用先用

startService()

啟動了這個服務,那麼要停止它,就必須使用

stopService()

了。不過在整個音樂的設計當中,我們并沒有想過要讓

MusicService

停止工作,而是隻要它運作起來了,就一直在背景待命。

至此,

MusicService

的架子已經搭建完畢。

/*******************************************************************/

* 版權聲明

* 本教程隻在CSDN和安豆網釋出,其他網站出現本教程均屬侵權。

*另外,我們還推出了Arduino智能硬體相關的教程,您可以在我們的網店跟我學Arduino程式設計中購買相關硬體。同時也感謝大家對我們這些碼農的支援。

*最後再次感謝各位讀者對

安豆

的支援,謝謝:)

/*******************************************************************/