文章目錄
生命週期
startService和stopService方式
使用startService啓動:
startService(Intent(this@MainActivity, MyService::class.java))
使用stopService停止:
stopService(Intent(this@MainActivity, MyService::class.java))
生命週期如下
啓動時的生命週期如下:
Service啓動後多次調用startService,只會執行onStartCommand,不會再次執行onCreate。生命週期如下:
bindService和unbindService方式
使用bindService啓動,unbindService停止:
class MainActivity : AppCompatActivity() {
private val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
Log.d("~~~",Thread.currentThread().stackTrace[2].methodName)
}
override fun onServiceDisconnected(name: ComponentName?) {
Log.d("~~~",Thread.currentThread().stackTrace[2].methodName)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnBindService.setOnClickListener {
bindService(Intent(this@MainActivity, MyService::class.java), connection, BIND_AUTO_CREATE)
}
btnUnbindService.setOnClickListener {
unbindService(connection)
}
}
}
生命週期如下:
Service啓動後多次執行bindService,不會執行任何回調。
如果一個Service既被startService啓動了,又被bindService綁定了,那麼單獨調用stopService或unbindService都不會使Service銷燬,直到stopService和unbindService都被調用後Service纔會被銷燬。
也就是說,點擊Stop Service按鈕只會讓Service停止,點擊Unbind Service按鈕只會讓Service和Activity解除關聯,一個Service必須要在既沒有和任何Activity關聯又處於停止狀態的時候纔會被銷燬。
onRebind回調
Service中還有個onRebind回調,如果Service未被銷燬,並被同一個Activity多次綁定,並且onUnbind方法中返回的是true,則會執行onRebind回調。
例如:在MyService中編寫以下代碼:
class MyService : Service() {
val myBinder = MyBinder()
override fun onCreate() {
super.onCreate()
Log.d("~~~", Thread.currentThread().stackTrace[2].methodName)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d("~~~", Thread.currentThread().stackTrace[2].methodName)
return super.onStartCommand(intent, flags, startId)
}
override fun onBind(intent: Intent?): IBinder? {
Log.d("~~~", Thread.currentThread().stackTrace[2].methodName)
return myBinder
}
override fun onUnbind(intent: Intent?): Boolean {
Log.d("~~~",Thread.currentThread().stackTrace[2].methodName)
return true
}
override fun onRebind(intent: Intent?) {
super.onRebind(intent)
Log.d("~~~",Thread.currentThread().stackTrace[2].methodName)
}
override fun onDestroy() {
super.onDestroy()
Log.d("~~~", Thread.currentThread().stackTrace[2].methodName)
}
inner class MyBinder : Binder() {
fun startTask() {
Log.d("~~~", Thread.currentThread().stackTrace[2].methodName)
}
}
}
在MainActivity中編寫如下代碼:
class MainActivity : AppCompatActivity() {
private lateinit var myBinder:MyService.MyBinder
private val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
Log.d("~~~",Thread.currentThread().stackTrace[2].methodName)
myBinder = service as MyService.MyBinder
myBinder.startTask()
}
override fun onServiceDisconnected(name: ComponentName?) {
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initView()
}
private fun initView() {
btnStartService.setOnClickListener {
startService(Intent(this@MainActivity, MyService::class.java))
}
btnStopService.setOnClickListener {
stopService(Intent(this@MainActivity, MyService::class.java))
}
btnBindService.setOnClickListener {
bindService(Intent(this@MainActivity, MyService::class.java), connection, BIND_AUTO_CREATE)
}
btnUnbindService.setOnClickListener {
unbindService(connection)
}
}
}
運行程序,查看Log控制檯。
點擊startService:
~~~: onCreate
~~~: onStartCommand
點擊bindService:
~~~: onBind
~~~: onServiceConnected
~~~: startTask
點擊unbindService:
~~~: onUnbind
再點擊bindService:
~~~: onServiceConnected
~~~: startTask
~~~: onRebind
再點擊unbindService:
~~~: onUnbind
點擊stopService:
~~~: onDestroy
Service 和 Thread
Service作爲四大組件之一,本身和Thread是沒有任何關係的。Service就像是一個沒有界面的Activity,默認在主線程中運行,如果要在Service中執行耗時任務的話,需要在Service中開一個子線程。例如:
class MyService : Service() {
private val myBinder = MyBinder()
private val timerTask by lazy {
object : TimerTask() {
override fun run() {
Log.d("~~~", "${System.currentTimeMillis()}")
}
}
}
override fun onBind(intent: Intent?): IBinder? {
return myBinder
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// 執行耗時任務,這裏開啓了一個定時器模擬耗時任務
Thread(Runnable {
Timer().schedule(timerTask, 0, 1000)
}).start()
return super.onStartCommand(intent, flags, startId)
}
inner class MyBinder : Binder() {
fun startTask() {
// 執行耗時任務,這裏開啓了一個定時器模擬耗時任務
Thread(Runnable {
Timer().schedule(timerTask, 0, 1000)
}).start()
}
}
override fun onDestroy() {
super.onDestroy()
timerTask.cancel()
}
}
在Service中開啓子線程的好處是Service可以全局共享,多個
Activity綁定Service時,獲取到的是同一個binder實例。
前臺 Service
由於Service默默地在後臺運行,優先級比較低,在系統內存不足時,系統會根據進程優先級從低到高來殺進程。如果想要Service不被系統輕易殺死,可以使用前臺Service。
前臺Service最明顯的區別就在於他會在通知欄顯示當前Service正在運行,優先級較高。來看一張效果圖:
使用前臺Service需要添加權限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
Android8.0之後,通知欄需要添加channelId,使用前臺Service示例如下:
class MyService : Service() {
private val myBinder = MyBinder()
override fun onBind(intent: Intent?): IBinder? {
return myBinder
}
override fun onCreate() {
super.onCreate()
val notificationIntent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0)
val channelId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel("my_service", "My Background Service")
} else {
// If earlier version channel ID is not used
""
}
val builder = NotificationCompat.Builder(this, channelId)
val notification = builder.setOngoing(true)
.setContentTitle("content title")
.setContentText("content text")
.setContentIntent(pendingIntent)
.setSmallIcon(R.mipmap.ic_launcher)
.setPriority(PRIORITY_MIN)
.build()
startForeground(1, notification)
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(channelId: String, channelName: String): String {
val chan = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_NONE)
chan.lightColor = Color.BLUE
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
service.createNotificationChannel(chan)
return channelId
}
inner class MyBinder : Binder() {
fun startTask() {
}
}
}
pendingIntent用於設置通知欄點擊事件,channelID是Android8.0之後需要設置的參數,使用前要先調用NotificationManager的createNotificationChannel創建渠道。builder中可以設置通知欄的標題、內容、圖標等屬性。