手把手教你做音乐播放器(四)播放服务的搭建

第4节 播放服务的搭建

播放音乐的服务-MusicService是整改音乐播放器的核心,它将播放界面和实际的播放功能连接在一起。

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传递一点数据,除此之外,两者没有数据交换、没有其他的功能调用,这两个组件之间基本上互不影响。

设计这样的一个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会提供接口给其它模块调用,

设计这样的一个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(0);
        }
    
        @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.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编程中购买相关硬件。同时也感谢大家对我们这些码农的支持。

*最后再次感谢各位读者对安豆的支持,谢谢:)
/*******************************************************************/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值