Android-Start方式和Bind方式混合开启Service

Android-Start方式和Bind方式混合开启Service

需求如下

需要开发一个音乐APP,需要满足以下的需求:

  • 当退出所有的Activity后仍然能够播放音乐
  • 能够控制音乐的播放比如说,暂停,上一首,下一首,获取正在播放的音乐的信息等。

首先所有的Activity都退出后仍然要能够播放音乐,从这一点来看,我们肯定是需要一个服务的并且这个服务还得是通过Start的方式开启的(因为播放音乐需要长时间运行,Bind的方式显然不符合我们的需求)。但是因为我们有需要控制音乐的播放,这时候Bind方式也是不可或缺的(Start 方式没法与Service交互)。

现在,Start和Bind的方式都不能完全地满足我们的需求,那怎么办呢,当然是让他们两在一起了 😂。

建立服务

我们要为播放音乐建立一个MusicService

/**
 * 用于音乐播放的服务
 * */
class MusicService : Service() {
    //与Service想关联的Binder
    private val mBinder = MusicBinder()
    //音乐列表
    private var singList: List<String>? = null
    //音乐的索引
    private var index = -1
    //表示音乐播放是否暂停
    private var paused = false
    //表示服务是否处于运行状态
    private var isRunning = false
    //播放事件的标记,每首歌为10秒
    var playingCountDown = 0

    override fun onBind(intent: Intent?): IBinder {
        return this.mBinder
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        //开启线程播放音乐
        thread {
            while (isRunning) {
                if (paused) {
                    "Music playing has been paused!".logE()
                } else {
                    "${this.singList!![index]} are playing".logE()
                    playingCountDown++
                    //每十秒切换下一首音乐
                    if (playingCountDown >= 10) {
                        playingCountDown = 0
                        index++
                        if (index >= this.singList!!.size)
                            index = 0
                    }
                }
                //线程睡眠一秒
                SystemClock.sleep(1000)
            }
        }
        return super.onStartCommand(intent, flags, startId)
    }

    //当服务创建的时候初始化一些数据
    override fun onCreate() {
        super.onCreate()
        this.singList = listOf("My heart will go on", "清明上河图", "夜的钢琴曲 五", "彩云追月")
        this.index = 0
        this.isRunning = true
    }

    //当服务销毁的时候回收资源
    override fun onDestroy() {
        super.onDestroy()
        this.isRunning = false
        this.singList = null
    }

    inner class MusicBinder : Binder() {

        fun pause() {
            this@MusicService.paused = true
        }

        fun play() {
            this@MusicService.paused = false
        }

        fun next() {
            this@MusicService.index++
            if (this@MusicService.index >= this@MusicService.singList?.size!!) {
                this@MusicService.index = 0
            }
            this@MusicService.playingCountDown = 0
        }

        fun previous() {
            this@MusicService.index--
            if (this@MusicService.index < 0)
                index = this@MusicService.singList!!.size - 1
            this@MusicService.playingCountDown = 0
        }

        fun getSingInfo(): String {
            return "Current sing is ${singList!![index]}"
        }
    }
}

上面的代码虽然有点长,但是都比较简单,其中使用打Log的方式模拟了播放音乐,通过Binder暴露出来了一系列的方法,来控制音乐的播放,在onStartCommand方法中我们开启了一个线程来播放音乐,之所以是要开启线程,是因为播放音乐是一个持续性的工作,不能够在主线程运行。

Activity的代码

首先是布局文件,布局文件没有什么特殊的,仅仅是几个Button来控制音乐的播放

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="top.littledavid.musicservice.MainActivity">

    <Button
        android:id="@+id/prepareBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Prepare"
        android:textAllCaps="false" />

    <Button
        android:id="@+id/pauseBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Pause"
        android:textAllCaps="false" />

    <Button
        android:id="@+id/nextBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Next"
        android:textAllCaps="false" />

    <Button
        android:id="@+id/previousBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Previous"
        android:textAllCaps="false" />

    <Button
        android:id="@+id/getInfoBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Get Info"
        android:textAllCaps="false" />
</LinearLayout>

Activity的代码

class MainActivity : AppCompatActivity(), View.OnClickListener {

    var mBinder: MusicService.MusicBinder? = null

    var serviceConn = object : ServiceConnection {
        override fun onServiceDisconnected(name: ComponentName?) {
        }

        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            mBinder = service as MusicService.MusicBinder
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //注册点击事件
        val btnList = listOf<Int>
        (R.id.prepareBtn, R.id.pauseBtn, R.id.previousBtn, R.id.nextBtn, R.id.getInfoBtn)
        btnList.forEach {
            this.findViewById<Button>(it).setOnClickListener(this)
        }
    }

    //各个按钮的点击事件
    override fun onClick(v: View?) {
        when (v!!.id) {
            R.id.prepareBtn -> {//准备播放
                //通过混合开启的方式开启服务
                val serviceIntent = Intent(this, MusicService::class.java)
                this.startService(serviceIntent)
                this.bindService(serviceIntent, this.serviceConn, Context.BIND_AUTO_CREATE)
            }
            R.id.pauseBtn -> { //开始 or 暂停播放
                //开始播放
                if (pauseBtn.text == "Start") {
                    mBinder!!.play()
                    pauseBtn.text = "Pause"
                } else {
                    mBinder!!.pause()
                    pauseBtn.text = "Start"
                }
            }
            R.id.previousBtn -> { //上一首
                mBinder!!.previous()
            }
            R.id.nextBtn -> {   //下一首
                mBinder!!.next()
            }
            R.id.getInfoBtn -> {
                mBinder!!.getSingInfo() show this
            }
        }
    }

    //回收mBinder
    override fun onDestroy() {
        super.onDestroy()
        this.unbindService(serviceConn)
        this.mBinder = null
    }
}

上面就时Activity的代码,主要就是通过一系列的按钮来控制音乐的播放。对了不要忘记在 Manifest 文件中配置我们的服务。

首先是建立的匿名的内部类来获取Binder,这个不用过多解释。然后在onCreate方法为一系列的按钮注册了事件。

在 Prepare Button的点击事件中,我们通过start和bind的方式同时开启了Service。或许你会担心,服务会被创建两次,请不必担心,因为在start方式开启service的时候Service已经被创建,所以在通过Bind方式开启服务的时候服务不会被再次创建,仅仅获取了Binder。

在获取了Binder以后,Activity和Service之间就建立了紧密的链接,我们可以通过Binder来控制Service的行为(音乐播放)。

因为我们是混合开启了服务,所以当我们退出Activity以后Log依然在不停的打印,这里因为在onDestroy方法中仅仅是解除了Activity与Service的关联,并不能将Service关闭。这样就实现了我们的需求,可以控制音乐的播放,同时也可以在退出Activity的时候仍然可以继续播放。

如何关闭混合方式开启的服务

在上面的音乐播放服务的Demo中,我们在Activity的 onDestroy 的生命周期方法中调用了 unbindService 方法,但是服务并没有关闭,那么混合方式开启的服务该怎么关闭呢?当然是混合开启,混合关闭了 😥-_-!!.

我们再在Activity中添加一个Button来事件我们关闭服务的业务逻辑:

this.stopService(Intent(this, MusicService::class.java))
this.unbindService(serviceConn)
mBinder = null

this.isShutDown = true

源码下载

源码已经上传至Github 下载地址如下:
https://github.com/littledavid-tech/MusicService

posted @ 2018-07-26 11:08  鲁迅认识的那只猹  阅读(733)  评论(0编辑  收藏  举报