1.什么是Service?
Service即服务,服务就是无用户界面也可以运行在后台的组件。与Activity不同的是:一直在后台运行,没有用户界面。
2.使用Service还是新线程
前提:需要在主线程外执行耗时工作
- 需要与用户交互时,创建新线程执行。例:Activity运行的同时播放音乐。
- 无需与用户进行交互,创建Service执行。
注意:Service运行在主线中,如果进行耗时操作则需开启子线程执行操作。
3.Service生命周期
引用Google官方详解图:
注意:与 Activity 生命周期回调方法不同,您不需要调用这些回调方法的超类实现。
具体方法:
- onCreate() 首次创建服务时,调用此方法。如果服务已经在运行,不会在调用此方法。
- onStartCommand() 当组件通过startService()启动服务时,调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。如需停止则调用sotpSelf()或者stopService().多次start多次回调。
- onBind() 当组件通过调用bindService()与服务绑定时,回调此方法。如果不允许绑定则返回null.
- onUnbind() 当Service上绑定的所有客户端都断开时将会回调该方法。
- onDestroy() 当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。
4.Service使用
- 定义一个继承Service的子类
- AndroidManifest.xml中声明
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>
android:name
属性是唯一必需的属性,用于指定服务的类名
android:exported
属性并将其设置为 “false”,确保服务仅适用于您的应用
也可以在< Service>标签中定义< intent-fliter>,不过在Android 5.0之后只能显式的启动服务
运行Service的两种方式:
- 通过Context.startService()方法,与启动者无关联,单独的生命周期
- 通过Context.bindService()方法,与启动这相关联,关联着退出,随之终止
使用:
MyService.kt
package com.wdl.crazyandroiddemo.service
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import android.util.Log
class MyService : Service() {
private val binder:Binder = MyBinder()
private class MyBinder : Binder() {
fun count() = 5
}
override fun onBind(intent: Intent): IBinder? {
Log.e("wdl", "------onBind()-----")
return binder
}
override fun onCreate() {
super.onCreate()
Log.e("wdl", "------onCreate()-----")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.e("wdl", "------onStartCommand()-----")
return super.onStartCommand(intent, flags, startId)
}
override fun onUnbind(intent: Intent?): Boolean {
Log.e("wdl", "------onUnbind()-----")
return true
}
override fun onRebind(intent: Intent?) {
super.onRebind(intent)
Log.e("wdl", "------onRebind()-----")
}
override fun onDestroy() {
super.onDestroy()
Log.e("wdl", "------onDestroy()-----")
}
}
测试demo
package com.wdl.crazyandroiddemo
import android.app.Service
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import com.wdl.crazyandroiddemo.service.MyService
import kotlinx.android.synthetic.main.activity_service.*
class ServiceActivity : AppCompatActivity() {
private val conn = object :ServiceConnection{
override fun onServiceDisconnected(name: ComponentName?) {
Log.e("wdl","异常断开连接-------")
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
Log.e("wdl","连接-------")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_service)
mStart.setOnClickListener {
val intent = Intent(this,MyService::class.java)
startService(intent)
}
mStop.setOnClickListener {
val intent = Intent(this,MyService::class.java)
stopService(intent)
}
mBindService.setOnClickListener {
val intent = Intent(this,MyService::class.java)
bindService(intent,conn,Service.BIND_AUTO_CREATE)
}
mUnBindService.setOnClickListener {
unbindService(conn)
}
}
}
点击第一个和第三个按钮,启动Service和停止Service。
查看日志:
startService后多次重复点击,只执行onStartCommand()方法。
bindService后通过unBindService解除绑定。
通过第二种方法绑定服务时,完整的方法为bindService(Intent intent,ServiceConnection conn,int flags)
对应参数为:
- intent 指定的service
- conn ServiceConnection 对象,用于监听访问者与Service之间的连接情况。连接
异常时
,回调onServiceDisconnected(name: ComponentName?)
- flag 指定绑定时是否自动创建Service(如果还未创建),0不自动创建,BIND_AUTO_CREATE自动创建
多个客户端可以同时绑定到服务。客户端完成与服务的交互后,会调用 unbindService(),取消绑定。一旦没有客户端绑定到该服务,系统就会销毁它。
要创建绑定服务,必须实现 onBind() 回调方法以返回 IBinder,您可以绑定到已经使用
startService()
启动的服务。例如,可以通过使用 Intent(标识要播放的音乐)调用 startService() 来启动后台音乐服务。随后,可能在用户需要稍加控制播放器或获取有关当前播放歌曲的信息时,Activity 可以通过调用bindService()
绑定到服务。在这种情况下,除非所有客户端均取消绑定,否则stopService() 或 stopSelf()
不会实际停止服务。
5.扩展
- ·
Service
这是适用于所有服务的基类。扩展此类时,必须创建一个用于执行所有服务工作的新线程
,因为默认情况下,服务将使用应用的主线程,这会降低应用正在运行的所有 Activity 的性能。 IntentService
这是 Service 的子类,它使用工作线程
逐一处理所有启动请求。如果您不要求服务同时处理多个请求,这是最好的选择。 您只需实现onHandleIntent()
方法即可,该方法会接收每个启动请求的 Intent,使您能够执行后台工作。
IntentService特性:
- 创建
默认的工作线程
,用于在应用的主线程外
执行传递给 onStartCommand() 的所有 Intent。 - 创建
工作队列
,用于将 Intent 逐一传递给onHandleIntent()
实现 - 在处理完所有启动请求后
自动停止服务
,因此您永远不必调用 stopSelf()。 - 提供 onBind() 的默认实现(返回 null)。
- 供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现。
案例:
package com.wdl.crazyandroiddemo.service
import android.app.IntentService
import android.content.Intent
import android.util.Log
/**
* An [IntentService] subclass for handling asynchronous task requests in
* a service on a separate handler thread.
* TODO: Customize class - update intent actions and extra parameters.
*/
class MyIntentService : IntentService("MyIntentService") {
override fun onHandleIntent(intent: Intent?) {
//直接执行耗时操作
val endTime = System.currentTimeMillis()+20*1000
Log.e("wdl","onHandleIntent")
while (System.currentTimeMillis()<endTime){
synchronized(this){
Thread.sleep(endTime-System.currentTimeMillis())
Log.e("wdl","synchronized")
}
}
}
}
onStartCommand()
方法必须返回整型数,用于描述系统应该如何在服务终止的情况下继续运行服务
- START_NOT_STICKY:
所在进程执行了onStartCommon后被销毁,不会重新创建Service
如果系统在 onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。 - START_STICKY:
所在进程执行了onStartCommon后被销毁,重新创建Service,并执行onStartCommon,但是传入的intent为空
如果系统在 onStartCommand() 返回后终止服务,则会重建服务并调用 onStartCommand(),但不会重新传递最后一个 Intent。相反,除非有挂起 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用 onStartCommand()。这适用于不执行命令、但无限期运行
并等待作业的媒体播放器(或类似服务)。 - START_REDELIVER_INTENT:
所在进程执行了onStartCommon后被销毁,重新创建Service并执行onStartCommon,但是传入的intent为上次的intent
如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业
(例如下载文件)的服务。
如果服务同时处理
多个 onStartCommand()
请求,则您不应在处理完一个启动请求之后停止服务,因为您可能已经收到了新的启动请求(在第一个请求结束时停止服务会终止第二个请求)。为了避免这一问题,您可以使用stopSelf(int)
确保服务停止请求始终基于最近的启动请求。也就说,在调用 stopSelf(int) 时,传递与停止请求的 ID 对应的启动请求的 ID(传递给 onStartCommand() 的 startId)。然后,如果在您能够调用 stopSelf(int) 之前服务收到了新的启动请求,ID 就不匹配,服务也就不会停止。
6.前台服务
如果希望服务一直保持运行状态,而不会因为系统内存不足导致被回收,就可以考虑使用前台服务。前台服务与后台服务最大的区别是,他会有一个正在运行的图标在系统的状态栏显示,下拉可以看到更加详细的信息。
override fun onCreate() {
super.onCreate()
Log.e("wdl", "------onCreate()-----")
val intent = Intent(this,ServiceActivity::class.java)
val pi = PendingIntent.getActivity(this,0,intent,0)
val notification = Notification(R.drawable.ic_launcher_background,"hello",System.currentTimeMillis())
notification.contentIntent = pi
startForeground(1,notification)
}
通过使用
- startForeground(1,notification) 开启前台服务,第一个参数不能为0
- stopForeground(boolean) 参数代表是否将通知从状态栏中移除