这是我参与「第四届青训营 」笔记创作活动的的第4天.
Service简介
Service是android 系统中的四大组件之一,需要在Manifest注册
Service只能后台运行,可以和其他组件进行交互。
这也可以实现App在后台服务依然在进行的效果,就如同音乐播放器、下载器
Service 的生命周期与启动方式
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播放器
实现效果
懂不懂我理塘王子的魅力啊😤😤😤😤
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)
尽情享受纯真的音乐吧(逃
总结
其实这套步骤还是有点烦的
我本着好记性不如烂笔头的原则
我把以上步骤整理成了简单的思维导图
The end