9.1 KiB
Service的生命週期
Service的啟動方式
Service由startService()啟動之後,便獨立動作,啟動者(例如某個activity)無法取得Service的intance,也無法呼叫Service的API。Service可以被多次呼叫startService(),但是只要一旦stopService()被呼叫了,Service就會結束。所以需要確保Service的管理者是誰,統一管理者來呼叫startService()與stopService()才不會造成混亂。
bindService()則是像是典型的Server-Client架構,第一次bindService()的時候會建立Service的instance,然後可以多次bindService()與unbindService(),當Service沒也任何人跟它"Bind"的時候,Service才會結束。
startService
要建立自己的Service,需要繼承Service()類別,然後複寫4個成員函式:
class MyService : Service() {
override fun onCreate() {
Log.i("Awin","onCreate - Thread ID = " + Thread.currentThread().id)
super.onCreate()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.i("Awin", "onStartCommand - startId = " + startId + ", Thread ID = " + Thread.currentThread().id)
return super.onStartCommand(intent, flags, startId)
}
override fun onBind(p0: Intent?): IBinder? {
Log.i("Awin", "onBind - Thread ID = " + Thread.currentThread().id)
return null // 因為目前沒有要支援bindService(),所以這裡直接return null
}
override fun onDestroy() {
Log.i("Awin", "onDestroy - Thread ID = " + Thread.currentThread().id)
super.onDestroy()
}
}
假設現在由MainActivity呼叫startService(),因為是第一次呼叫,所以MyService的callback被呼叫的順序是:onCreate() -> onStartCommand()。
第二次之後的呼叫就只會執行onStartCommand()。
當MainActivity呼叫stopService()時,則會執行MyService的onDestroy()。
所以,必須將「長期任務」開個 Thread 並且執行在 onStartCommand() 方法內。
Client端的code:
val intent = Intent(this, ServiceDemo.class)
startService(intent)
bindService
使用bindService()來建立Service的一個好處就是可以取得Service的instance,然後藉由這個instance來使用Service的API。
要使用bindService()的話,Service類別必須實做onBind()與onUnbind()至兩個override function。
也必須提供一個Binder class(繼承自Binder()),來讓client呼叫。例:
class MyService : Service() {
//client 可以通过Binder获取Service实例
inner class MyBinder : Binder() {
val service: MyService
get() = this@MyService
}
//通过binder实现调用者client与Service之间的通信
private val binder = MyBinder()
private val generator: Random = Random()
override fun onCreate() {
Log.i("xiao", "MyService - onCreate - Thread = " + Thread.currentThread().name)
super.onCreate()
}
/**
* @param intent 啟動時,啟動組件傳遞過來的Intent,如Activity可利用Intent封裝所需要的參數並傳遞給Service
* @param flags 表示啟動請求時是否有額外數據,可選值有 0,START_FLAG_REDELIVERY,START_FLAG_RETRY
* 0: 在正常創建Service的情況下,onStartCommand傳入的flags為0。
*
* START_FLAG_REDELIVERY:
* 這個值代表了onStartCommand()方法的返回值為 START_REDELIVER_INTENT,
* 而且在上一次服務被殺死前會去調用stopSelf()方法停止服務。
* 其中START_REDELIVER_INTENT意味著當Service因記憶體不足而被系統kill後,
* 則會重建服務,並透過傳遞給服務的最後一個 Intent調用 onStartCommand(),此時Intent時有值的。
*
* START_FLAG_RETRY
* 該flag代表當onStartCommand()調用後一直沒有返回值時,會嘗試重新去調用onStartCommand()。
*
* @param startId 指明當前服務的唯一ID,與stopSelfResult(int startId)配合使用,stopSelfResult()可以更安全地根據ID停止服務。
*
* @return
* START_STICKY:
* 當Service因記憶體不足而被系統kill後,一段時間後記憶體再次空閒時,
* 系統將會嘗試重新創建此Service,一旦創建成功後將回調onStartCommand方法,
* 但其中的Intent將是null,除非有掛起的Intent,如pendingintent,
* 這個狀態下比較適用於不執行命令、但無限期運行並等待作業的媒體播放器或類似服務
*
*
* START_NOT_STICKY:
* 當Service因記憶體不足而被系統kill後,即使系統記憶體再次空閒時,
* 系統也不會嘗試重新創建此Service。除非程序中再次調用startService啟動此Service,
* 這是最安全的選項,可以避免在不必要時以及應用能夠輕鬆重啟所有未完成的作業時運行服務。
*
* START_REDELIVER_INTENT:
* 當Service因記憶體不足而被系統kill後,則會重建服務,
* 並透過傳遞給服務的最後一個 Intent 調用 onStartCommand(),任何掛起 Intent均依次傳遞。
* 與START_STICKY不同的是,其中的傳遞的Intent將是非空,是最後一次調用startService中的intent。
* 這個值適用於主動執行應該立即恢復的作業(例如下載文件)的服務。
*/
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.i("xiao", "MyService - onStartCommand - startId = $startId, Thread = " + Thread.currentThread().name)
return START_NOT_STICKY
}
override fun onBind(intent: Intent): IBinder{
Log.i("xiao", "MyService - onBind - Thread = " + Thread.currentThread().name)
return binder
}
override fun onUnbind(intent: Intent): Boolean {
Log.i("xiao", "MyService - onUnbind - from = " + intent.getStringExtra("from"))
return super.onUnbind(intent)
}
override fun onDestroy() {
Log.i("xiao", "MyService - onDestroy - Thread = " + Thread.currentThread().name)
super.onDestroy()
}
//getRandomNumber是Service暴露出去供client调用的公共方法
fun getRandomNumber(): Int {
return generator.nextInt()
}
}
Client端要做的事:
- 建立ServiceConnection類型實例,並覆載
onServiceConnected()方法和onServiceDisconnected()方法。 - 當執行到onServiceConnected callback時,可通過IBinder instance得到Service的instance,這樣可實現client與Service的連接。
- onServiceDisconnected callback被執行時,表示client與Service已經斷開,在此可以寫一些斷開後需要做的處理。
class AActivity : AppCompatActivity() {
private var service: MyService? = null
private var isBind = false
private var conn = object : ServiceConnection{
override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
isBind = true
val myBinder = p1 as MyService.MyBinder
service = myBinder.service
Log.i("xiao", "ActivityA - onServiceConnected")
val num = service!!.getRandomNumber()
Log.i("xiao", "ActivityA - getRandomNumber = $num");
}
override fun onServiceDisconnected(p0: ComponentName?) {
isBind = false
Log.i("xiao", "ActivityA - onServiceDisconnected")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_a)
Log.i("xiao", "ActivityA - onCreate - Thread = " + Thread.currentThread().name)
btn_bind_service_a.setOnClickListener {
val intent = Intent(this,MyService::class.java)
intent.putExtra("from","ActivityA")
Log.i("xiao", "----------------------------------------")
Log.i("xiao", "ActivityA 执行 bindService");
bindService(intent, conn, Context.BIND_AUTO_CREATE)
}
btn_unbind_service_a.setOnClickListener {
if(isBind){
Log.i("xiao", "----------------------------------------")
Log.i("xiao", "ActivityA 执行 unbindService");
unbindService(conn)
}
}
btn_a_start_b.setOnClickListener {
val intent = Intent(this,BActivity::class.java)
Log.i("xiao", "----------------------------------------")
Log.i("xiao", "ActivityA 启动 ActivityB");
startActivity(intent)
}
btn_finish_a.setOnClickListener {
Log.i("xiao", "----------------------------------------")
Log.i("xiao", "ActivityA 执行 finish");
finish()
}
}
override fun onDestroy() {
super.onDestroy()
Log.i("xiao", "ActivityA - onDestroy")
}
}
