昨天已經能看 Shizuku 有沒有在跑,但要手動更新
這很不智慧
就像門鈴壞掉,每五分鐘自己開門看有沒有人
今天改成讓 Shizuku 主動通知我們
Shizuku 提供三個重要事件:
private val binderReceived =
Shizuku.OnBinderReceivedListener { refresh() }
private val binderDead =
Shizuku.OnBinderDeadListener {
shellService = null
refresh()
}
private val permissionResult =
Shizuku.OnRequestPermissionResultListener { _, _ ->
refresh()
}
分別代表:
註冊時要用 Sticky 版本:
fun register() {
Shizuku.addBinderReceivedListenerSticky(binderReceived)
Shizuku.addBinderDeadListener(binderDead)
Shizuku.addRequestPermissionResultListener(permissionResult)
refresh()
}
因為 Binder 可能在 Application.onCreate() 前就送到了
不用 Sticky 的話,就像派對結束後才去門口等邀請函
你可以等很久,真的
這些 listener 放在 Application 註冊,不要綁在 Activity
不然 Activity 被回收,監聽也跟著登出人生
服務活著,不代表我們有權使用:
private fun currentState(): ShizukuState {
val alive = runCatching {
Shizuku.pingBinder()
}.getOrDefault(false)
if (!alive) {
return if (isShizukuInstalled())
ShizukuState.NotRunning
else
ShizukuState.NotInstalled
}
if (Shizuku.isPreV11())
return ShizukuState.Unsupported
val granted = Shizuku.checkSelfPermission() ==
PackageManager.PERMISSION_GRANTED
return if (granted)
ShizukuState.Ready
else
ShizukuState.PermissionRequired
}
需要授權時呼叫:
fun requestPermission() {
runCatching {
Shizuku.requestPermission(REQUEST_CODE)
}
}
使用者按下同意後,listener 會收到結果,重新整理狀態,畫面就會自己變成 Ready
終於不用一直問「好了沒」
連 App 都受不了這種對話
ShizukuManager 用 StateFlow,不直接用 Compose 的 mutableStateOf
因為它不該知道畫面是 Compose、Widget 還是其他東西
資料層如果開始管 UI,最後會像親戚群組一樣,什麼都管
Compose 裡再用:
collectAsStateWithLifecycle()
這樣畫面進背景後會停止收集,比較不浪費資源
完成後記得測:
如果 Shizuku 停掉後畫面會自己更新,就代表 Binder 的生死我們接住了
明天把這些狀態做成真正的畫面
不再只用一行 Text 敷衍使用者