Service简介
Service是Android中实现程序后台运行的解决方案,适合执行不需要和用户交互而且还要求长期运行的任务,不依赖于任何用户界面,即使程序被切换到后台,仍然能够保持正常运行
Service并不是运行在一个独立的进程当中的,而是依赖于创建Service时所在的应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的Service也会停止运行
Service不会自动开启线程,所有的代码都是默认运行在主线程当中的。我们需要在Service的内部手动创建子线程,并在这里执行具体的任务,否则就有可能出现主线程被阻塞的情况
Service简单用法
创建一个service
class MyService : Service() {
companion object {
private const val TAG = "MyService"
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
// 第一次创建的时候调用
override fun onCreate() {
Log.d(TAG, "onCreate: ")
super.onCreate()
}
// 每次启动的时候调用
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d(TAG, "onStartCommand: ")
return super.onStartCommand(intent, flags, startId)
}
// 销毁的时候调用
override fun onDestroy() {
Log.d(TAG, "onDestroy: ")
super.onDestroy()
}
}
启动和停止Service
class ServiceActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_service)
//启动Service
start_service.setOnClickListener {
val intent = Intent(this, MyService::class.java)
startService(intent)
}
//停止Service,Service也可以自我停止运行,只需要在Service内部调用stopSelf()方法即可
stop_service.setOnClickListener {
val intent = Intent(this, MyService::class.java)
stopService(intent)
}
}
}
从Android 8.0系统开始,应用的后台功能被大幅削减。现在只有当应用保持在前台可见状态的情况下,Service才能保证稳定运行,一旦应用进入后台之后,Service随时都有可能被系统回收。如果你真的需要长期在后台执行一些任务,可以使用前台Service或者WorkManager
Activity和Service进行通信
例如:在MyService里提供一个下载功能,然后在Activity中可以决定何时开始下载,以及随时查看下载进度
使用 Binder
解决方法是创建一个专门的Binder对象来对下载功能进行管理
class MyService : Service() {
companion object {
private const val TAG = "MyService"
}
private val mBinder = DownloadBinder()
inner class DownloadBinder : Binder() {
fun startDownload() {
Log.d(TAG, "startDownload: ")
}
fun getProgress(): Int {
Log.d(TAG, "getProgress: ")
return 0
}
}
override fun onBind(intent: Intent?): IBinder? {
return mBinder
}
......
}
绑定和解绑Service
class ServiceActivity : AppCompatActivity() {
companion object {
private const val TAG = "ServiceActivity"
}
private val connection = object : ServiceConnection {
// 会在Activity与Service成功绑定的时候调用
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
Log.d(TAG, "onServiceConnected: ")
val downloadBinder = service as MyService.DownloadBinder
downloadBinder.startDownload()
downloadBinder.getProgress()
}
//只有在Service的创建进程崩溃或者被杀掉的时候才会调用
override fun onServiceDisconnected(name: ComponentName?) {
Log.d(TAG, "onServiceDisconnected: ")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_service)
// 绑定Service
bind_service.setOnClickListener {
val intent = Intent(this, MyService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
// 解绑Service
unbind_service.setOnClickListener {
unbindService(connection)
}
}
}
bindService()方法接收3个参数,第一个参数就是刚刚构建出的Intent对象,第二个参数是前面创建出的ServiceConnection的实例,第三个参数则是一个标志位,这里传入BIND_AUTO_CREATE表示在Activity和Service进行绑定后自动创建Service。这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行
Service的生命周期
896629-20171003150906568-1811048252.png
如果对一个Service既调用了startService()方法,又调用了bindService()方法的,要同时调用stopService()和unbindService()方法,onDestroy()方法才会执行
前台Service
只有当应用保持在前台可见状态的情况下,Service才能保证稳定运行,一旦应用进入后台之后,Service随时都有可能被系统回收。如果希望Service能够一直保持运行状态,就可以考虑使用前台Service。前台Service和普通Service最大的区别就在于,它一直会有一个正在运行的图标在系统的状态栏显示
前台Service示例
class MyService : Service() {
companion object {
private const val TAG = "MyService"
}
// 第一次创建的时候调用
override fun onCreate() {
Log.d(TAG, "onCreate: ")
super.onCreate()
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// NotificationChannel类和createNotificationChannel()方法都是Android 8.0系统中新增的API
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// 创建通知渠道
val notificationChannel =
NotificationChannel(
channelId,
"前台Service通知",
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager.createNotificationChannel(notificationChannel)
}
val notification = getNotification()
startForeground(1, notification)
}
private fun getNotification(): Notification {
val intent = Intent(this, ServiceActivity::class.java)
val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
//第一个参数是context,第二个参数是渠道ID,需要和我们在创建通知渠道时指定的渠道ID相匹配
return NotificationCompat.Builder(applicationContext, channelId).apply {
// 点击跳转后,取消通知的显示
setAutoCancel(true)
// 设置 PendingIntent,点击通知会执行 PendingIntent 里面的 Intent 的意图
setContentIntent(pendingIntent)
// 配置通知的标题、正文内容、图标
setContentTitle("this is content title")
setStyle(
NotificationCompat.BigPictureStyle()
.bigPicture(BitmapFactory.decodeResource(resources, R.drawable.big_image))
)
setStyle(
NotificationCompat.BigTextStyle()
.bigText("this is content text this is content text ")
)
// 小图标会显示在系统状态栏上,只能使用纯alpha图层的图片(.png)进行设置,否则只是一块灰色区域
setSmallIcon(R.drawable.small_icon)
// 当下拉系统状态栏时,就可以看到设置的大图标
setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.big_image))
}.build()
}
......
}
调用startForeground()方法后就会让MyService变成一个前台Service,并在系统状态栏显示出来,例如按 HOME 键将应用转到后台,MyService会继续运行,但如果按返回键退出应用,就会停止,因为整个应用已经退出了。从Android 9.0系统开始,使用前台Service必须在AndroidManifest.xml文件中进行权限声明
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
IntentService
IntentService 是一个异步的、会自动停止的Service
class MyIntentService : IntentService("MyIntentService") {
companion object {
private const val TAG = "MyIntentService"
}
override fun onHandleIntent(intent: Intent?) {
Log.d(TAG, "onHandleIntent: Thread is ${Thread.currentThread().name}")
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy: ")
}
}
// 启动 IntentService
start_intentservice.setOnClickListener {
val intent = Intent(this, MyIntentService::class.java)
startService(intent)
}
onHandleIntent()这个抽象方法,这个方法已经在子线程中运行,这个Service在运行结束后是会自动停止的,也是因为这点,在 onHandleIntent 方法中不要再开线程,否则这个线程可能无法正常执行,因为 onHandleIntent 方法结束后 Service 就结束了













网友评论