天天看点

手把手教你做音乐播放器(四)播放服务的搭建第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编程中购买相关硬件。同时也感谢大家对我们这些码农的支持。

*最后再次感谢各位读者对

安豆

的支持,谢谢:)

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