Service实践 - 制作一个简易Zood播放器 | 青训营笔记

174 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的的第4天.

Service简介

Service是android 系统中的四大组件之一,需要在Manifest注册
Service只能后台运行,可以和其他组件进行交互。
这也可以实现App在后台服务依然在进行的效果,就如同音乐播放器、下载器

Service 的生命周期与启动方式

image.png
startService 、bindService都可以启动Service,但其略有区别

startService

startService(intent:Intent)

用startService启动的服务和Activity基本没有关系了
这种情况下,只要应用不寄,那么Service会一直存在
如生命周期图所示
当startActivity首次启动,首先会创建一个Service实例,调用Service里的onCreate,随后调用onStartCommand。
后续再调用startActivity就会复用之前创建的实例,再调用onStartCommand方法。
在这种情况下想停止Service,要么调用StopService,要么关闭应用。
这种方式非常简单,就不过多讲解了。

bindService

这种启动方式允许Service与activity通信
当startActivity首次启动,首先会创建一个Service实例,调用Service里的onCreate,onBind。
之后再调用bindService则会复用Service实例,也不会再调用onBind,只会直接把IBinder对象返回给调用方。


bindService有三个参数

  • Intent service
  • ServiceConnection conn
  • int flags
    第一个参数是显式创建的Intent,第二个参数是ServiceConnection接口的实现,第三个参数指定当前服务连接方式,BIND_AUTO_CREATE表示在绑定后如服务没有开启则自动开启服务

接下来上代码,来动手做一个 **Zood播放器**

实践-Zood播放器

实现效果

Screenshot_2022-08-01-12-15-09-539_com.example.minimusicplayer.jpg

懂不懂我理塘王子的魅力啊😤😤😤😤

Activity的创建

首先创建一个MainActivity,和布局文件,这点没什么好说的
下面是布局文件的代码

<androidx.constraintlayout.widget.ConstraintLayout 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"
   tools:context=".MainActivity">

   <ImageView
       android:id="@+id/imageView"
       android:layout_width="300dp"
       android:layout_height="200dp"
       android:layout_marginTop="90dp"
       android:src="@drawable/zood_cover"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toTopOf="parent" />

   <Button
       android:id="@+id/buttonPlay"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginTop="44dp"
       android:layout_marginRight="30dp"
       app:layout_constraintRight_toRightOf="@+id/imageView"
       app:layout_constraintTop_toBottomOf="@+id/imageView"
       android:text="▶" />

   <Button
       android:id="@+id/buttonStop"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginTop="44dp"
       app:layout_constraintTop_toBottomOf="@+id/imageView"
       app:layout_constraintLeft_toLeftOf="@+id/imageView"
       android:layout_marginLeft="30dp"
       android:text="┃┃" />


</androidx.constraintlayout.widget.ConstraintLayout>

这里用了约束布局,这种布局创建复杂的UI特别简单,推荐去学习一哈

Service类的创建

再Android Studio里新建一个Service,给他起名为Service4me

别问为啥这名字,问就是我喜欢

由于我们需要与Activity通信(继续播放、暂停),所以这里选择的Service启动方式是bindService,于是要重写的函数就是onCreate,onBind和onUnBind

音乐播放组件

这里选择用MediaPlayer来播放音乐。MediaPlayer有好几种方式来绑定资源,比如说MediaPlayer.setDataSource(), 这种方法非常普适,可以加载本地音乐,也可以加载网络音乐。

但是这里只要播放一首音乐,所以我选择了更简单的创建方式-MediaPlayer.create(context:Context,ID)。
MediaPlayer用函数Start来播放,Stop停止,pause暂停,非常的简单。
设置了MediaPlayer后,要将资源加载到内存中,要调用函数MediaPlayer.prepare()。
但对于较大的文件,这种做法会阻塞线程,于是也可以选择方法MediaPlayer.prepareAsync()来异步加载。
在实践中,我发现如果直接调用prepare会导致IllegalStateException报错,需要额外做一些步骤,如下

try {
    mediaPlayer.stop()
    mediaPlayer.prepareAsync()

}catch (e : IllegalStateException){
    e.printStackTrace()
}

Service实现

在Service类中,先创建一个IBinder的实现

class MusicBinder(private val mediaPlayer: MediaPlayer,private val context: Context) : Binder() {
    fun getCommand(id : Int){
        when(id){
            1 ->{
                if (!mediaPlayer.isPlaying) {
                    mediaPlayer.start()
                    Toast.makeText(context, "Playing zood", Toast.LENGTH_SHORT).show()
                }
            }
            2 -> {
                if(mediaPlayer.isPlaying) {
                    mediaPlayer.pause()
                    Toast.makeText(context, "Stop Playing zood", Toast.LENGTH_SHORT).show()
                }
            }
            else ->{
                Log.e("MusicPlayer","ERROR!")
            }
        }
    }
}

这个实现里的函数是根据需要来创建的,我这里创建一个getCommand函数,如果得到值1就播放音乐,如果得到2就暂停。
在类中添加两个成员

    private lateinit var mediaPlayer: MediaPlayer
    private lateinit var musicBinder: MusicBinder

接着在onCreate里初始化

override fun onCreate() {
    mediaPlayer = MediaPlayer.create(this,R.raw.zood)
    musicBinder = MusicBinder(mediaPlayer,this)
    try {
            mediaPlayer.stop()
            mediaPlayer.prepareAsync()
    }catch (e : IllegalStateException){
        e.printStackTrace()
    }

}

重写哦那onBind函数,返回binder

override fun onBind(intent: Intent): IBinder {
    return musicBinder
}

启动Service

回到MainActivity,声明一个Binder。 再创建一个ServiceConnection的实现,在方法onServiceConnected里初始化Binder,接下来他会负责我们与Service的通信。

private lateinit var musicBinder : Service4me.MusicBinder

private val conn : ServiceConnection = object : ServiceConnection{
    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        musicBinder = service as Service4me.MusicBinder
    }
    override fun onServiceDisconnected(name: ComponentName?) {
    }
}

最后一步,在onCreate里创建我们的Intent,启动Service!

intent = Intent(this,Service4me::class.java)
bindService(intent,conn,Context.BIND_AUTO_CREATE)

尽情享受纯真的音乐吧(逃

总结

其实这套步骤还是有点烦的
我本着好记性不如烂笔头的原则
我把以上步骤整理成了简单的思维导图

7761A6BD786B240078DA665CC8AF5AE9.png

2EE3CE8244011FB2F50B7B641215C01F.png

The end