vault backup: 2025-07-22 22:14:08

Affected files:
Too many files to list
This commit is contained in:
2025-07-22 22:14:08 +08:00
parent 99f67788fc
commit 5d4e261181
202 changed files with 86 additions and 886 deletions

View File

@@ -0,0 +1,205 @@
## Service的生命週期
![[Pasted image 20220307103552.png]]
## 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個成員函式
```kotlin
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:
```kotlin
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呼叫。例
```kotlin
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 表示啟動請求時是否有額外數據,可選值有 0START_FLAG_REDELIVERYSTART_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端要做的事
1. 建立ServiceConnection類型實例並覆載`onServiceConnected()`方法和`onServiceDisconnected()`方法。
2. 當執行到onServiceConnected callback時可通過IBinder instance得到Service的instance這樣可實現client與Service的連接。
3. onServiceDisconnected callback被執行時表示client與Service已經斷開在此可以寫一些斷開後需要做的處理。
```kotlin
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")
}
}
```
---
## 參考
1. [如何使用Service(kotlin)](https://givemepass.blogspot.com/2015/10/service.html)
2. [Android kotlin service使用简析 - 簡書](https://www.jianshu.com/p/f9712b470b42)
3. [《Android》『Service』- 背景執行服務的基本用法 - 賽肥膩膩の娛樂生活誌](https://xnfood.com.tw/android-service/#skill_02)