perf: log lifecycle

This commit is contained in:
lisonge 2024-10-21 00:04:41 +08:00
parent dc54864751
commit 724ff77c97
9 changed files with 181 additions and 102 deletions

View File

@ -10,6 +10,7 @@ import kotlinx.coroutines.launch
import li.songe.gkd.util.OnChangeListen import li.songe.gkd.util.OnChangeListen
import li.songe.gkd.util.OnDestroy import li.songe.gkd.util.OnDestroy
import li.songe.gkd.util.OnTileClick import li.songe.gkd.util.OnTileClick
import li.songe.gkd.util.useLogLifecycle
class FloatingTileService : TileService(), OnDestroy, OnChangeListen, OnTileClick { class FloatingTileService : TileService(), OnDestroy, OnChangeListen, OnTileClick {
override fun onStartListening() { override fun onStartListening() {
@ -41,6 +42,7 @@ class FloatingTileService : TileService(), OnDestroy, OnChangeListen, OnTileClic
} }
init { init {
useLogLifecycle()
scope.launch { scope.launch {
combine( combine(
FloatingService.isRunning, FloatingService.isRunning,

View File

@ -45,6 +45,8 @@ import li.songe.gkd.notif.notifyService
import li.songe.gkd.permission.notificationState import li.songe.gkd.permission.notificationState
import li.songe.gkd.service.A11yService import li.songe.gkd.service.A11yService
import li.songe.gkd.util.LOCAL_HTTP_SUBS_ID import li.songe.gkd.util.LOCAL_HTTP_SUBS_ID
import li.songe.gkd.util.OnCreate
import li.songe.gkd.util.OnDestroy
import li.songe.gkd.util.SERVER_SCRIPT_URL import li.songe.gkd.util.SERVER_SCRIPT_URL
import li.songe.gkd.util.getIpAddressInLocalNetwork import li.songe.gkd.util.getIpAddressInLocalNetwork
import li.songe.gkd.util.keepNullJson import li.songe.gkd.util.keepNullJson
@ -54,46 +56,62 @@ import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.subsItemsFlow import li.songe.gkd.util.subsItemsFlow
import li.songe.gkd.util.toast import li.songe.gkd.util.toast
import li.songe.gkd.util.updateSubscription import li.songe.gkd.util.updateSubscription
import li.songe.gkd.util.useAliveFlow
import li.songe.gkd.util.useLogLifecycle
import java.io.File import java.io.File
class HttpService : Service() { class HttpService : Service(), OnCreate, OnDestroy {
private val scope = MainScope() override fun onBind(intent: Intent?) = null
override fun onCreate() {
super.onCreate()
onCreated()
}
override fun onDestroy() {
super.onDestroy()
onDestroyed()
}
val scope = MainScope().apply { onDestroyed { cancel() } }
private val httpServerPortFlow = storeFlow.map(scope) { s -> s.httpServerPort } private val httpServerPortFlow = storeFlow.map(scope) { s -> s.httpServerPort }
private var server: EmbeddedServer<CIOApplicationEngine, CIOApplicationEngine.Configuration>? = private var server: EmbeddedServer<CIOApplicationEngine, CIOApplicationEngine.Configuration>? =
null null
override fun onCreate() { init {
super.onCreate() useLogLifecycle()
isRunning.value = true useAliveFlow(isRunning)
localNetworkIpsFlow.value = getIpAddressInLocalNetwork()
scope.launchTry(Dispatchers.IO) { onCreated { localNetworkIpsFlow.value = getIpAddressInLocalNetwork() }
httpServerPortFlow.collect { port -> onDestroyed { localNetworkIpsFlow.value = emptyList() }
server?.stop()
server = try { onDestroyed {
scope.createServer(port).apply { start() } if (storeFlow.value.autoClearMemorySubs) {
} catch (e: Exception) { deleteSubscription(LOCAL_HTTP_SUBS_ID)
LogUtils.d("HTTP服务启动失败", e)
null
}
if (server == null) {
toast("HTTP服务启动失败,您可以尝试切换端口后重新启动")
stopSelf()
return@collect
}
httpNotif.copy(text = "HTTP服务-$port").notifyService(this@HttpService)
} }
} }
}
override fun onDestroy() { onCreated {
super.onDestroy() scope.launchTry(Dispatchers.IO) {
scope.cancel() httpServerPortFlow.collect { port ->
isRunning.value = false server?.stop()
localNetworkIpsFlow.value = emptyList() server = try {
if (storeFlow.value.autoClearMemorySubs) { scope.createServer(port).apply { start() }
deleteSubscription(LOCAL_HTTP_SUBS_ID) } catch (e: Exception) {
LogUtils.d("HTTP服务启动失败", e)
null
}
if (server == null) {
toast("HTTP服务启动失败,您可以尝试切换端口后重新启动")
stopSelf()
return@collect
}
httpNotif.copy(text = "HTTP服务-$port").notifyService(this@HttpService)
}
}
} }
} }
@ -109,8 +127,6 @@ class HttpService : Service() {
app.startForegroundService(Intent(app, HttpService::class.java)) app.startForegroundService(Intent(app, HttpService::class.java))
} }
} }
override fun onBind(intent: Intent?) = null
} }
@Serializable @Serializable

View File

@ -10,6 +10,7 @@ import kotlinx.coroutines.launch
import li.songe.gkd.util.OnChangeListen import li.songe.gkd.util.OnChangeListen
import li.songe.gkd.util.OnDestroy import li.songe.gkd.util.OnDestroy
import li.songe.gkd.util.OnTileClick import li.songe.gkd.util.OnTileClick
import li.songe.gkd.util.useLogLifecycle
class HttpTileService : TileService(), OnDestroy, OnChangeListen, OnTileClick { class HttpTileService : TileService(), OnDestroy, OnChangeListen, OnTileClick {
override fun onStartListening() { override fun onStartListening() {
@ -41,6 +42,7 @@ class HttpTileService : TileService(), OnDestroy, OnChangeListen, OnTileClick {
} }
init { init {
useLogLifecycle()
scope.launch { scope.launch {
combine( combine(
HttpService.isRunning, HttpService.isRunning,

View File

@ -1,26 +1,29 @@
package li.songe.gkd.debug package li.songe.gkd.debug
import android.annotation.SuppressLint
import android.app.Service import android.app.Service
import android.content.Context
import android.content.Intent import android.content.Intent
import com.blankj.utilcode.util.LogUtils import com.blankj.utilcode.util.LogUtils
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import li.songe.gkd.app import li.songe.gkd.app
import li.songe.gkd.notif.notifyService import li.songe.gkd.notif.notifyService
import li.songe.gkd.notif.screenshotNotif import li.songe.gkd.notif.screenshotNotif
import li.songe.gkd.util.OnCreate
import li.songe.gkd.util.OnDestroy
import li.songe.gkd.util.ScreenshotUtil import li.songe.gkd.util.ScreenshotUtil
import li.songe.gkd.util.componentName import li.songe.gkd.util.componentName
import li.songe.gkd.util.useAliveFlow
import li.songe.gkd.util.useLogLifecycle
import java.lang.ref.WeakReference
class ScreenshotService : Service() { class ScreenshotService : Service(), OnCreate, OnDestroy {
override fun onBind(intent: Intent?) = null override fun onBind(intent: Intent?) = null
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
isRunning.value = true onCreated()
screenshotNotif.notifyService(this)
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
try { try {
return super.onStartCommand(intent, flags, startId) return super.onStartCommand(intent, flags, startId)
@ -35,25 +38,31 @@ class ScreenshotService : Service() {
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
isRunning.value = false onDestroyed()
screenshotUtil?.destroy() }
screenshotUtil = null
private var screenshotUtil: ScreenshotUtil? = null
init {
useLogLifecycle()
useAliveFlow(isRunning)
onCreated { screenshotNotif.notifyService(this) }
onCreated { instance = WeakReference(this) }
onDestroyed { instance = WeakReference(null) }
onDestroyed { screenshotUtil?.destroy() }
} }
companion object { companion object {
suspend fun screenshot() = screenshotUtil?.execute() private var instance = WeakReference<ScreenshotService>(null)
val isRunning = MutableStateFlow(false)
@SuppressLint("StaticFieldLeak") suspend fun screenshot() = instance.get()?.screenshotUtil?.execute()
private var screenshotUtil: ScreenshotUtil? = null fun start(intent: Intent) {
fun start(context: Context = app, intent: Intent) {
intent.component = ScreenshotService::class.componentName intent.component = ScreenshotService::class.componentName
context.startForegroundService(intent) app.startForegroundService(intent)
} }
val isRunning = MutableStateFlow(false) fun stop() {
fun stop(context: Context = app) { app.stopService(Intent(app, ScreenshotService::class.java))
context.stopService(Intent(context, ScreenshotService::class.java))
} }
} }
} }

View File

@ -56,6 +56,7 @@ import li.songe.gkd.util.map
import li.songe.gkd.util.showActionToast import li.songe.gkd.util.showActionToast
import li.songe.gkd.util.storeFlow import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.toast import li.songe.gkd.util.toast
import li.songe.gkd.util.useLogLifecycle
import li.songe.selector.MatchOption import li.songe.selector.MatchOption
import li.songe.selector.Selector import li.songe.selector.Selector
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
@ -89,6 +90,7 @@ class A11yService : AccessibilityService(), OnCreate, OnA11yConnected, OnA11yEve
val scope = CoroutineScope(Dispatchers.Default).apply { onDestroyed { cancel() } } val scope = CoroutineScope(Dispatchers.Default).apply { onDestroyed { cancel() } }
init { init {
useLogLifecycle()
useRunningState() useRunningState()
useAliveView() useAliveView()
useCaptureVolume() useCaptureVolume()
@ -485,19 +487,19 @@ private fun A11yService.useRuleChangedLog() {
} }
private fun A11yService.useRunningState() { private fun A11yService.useRunningState() {
A11yService.weakInstance = WeakReference(this) onCreated {
A11yService.isRunning.value = true A11yService.weakInstance = WeakReference(this)
if (!storeFlow.value.enableService) { A11yService.isRunning.value = true
// https://github.com/gkd-kit/gkd/issues/754 if (!storeFlow.value.enableService) {
storeFlow.update { it.copy(enableService = true) } // https://github.com/gkd-kit/gkd/issues/754
storeFlow.update { it.copy(enableService = true) }
}
ManageService.autoStart()
} }
onDestroyed { onDestroyed {
if (storeFlow.value.enableService) { if (storeFlow.value.enableService) {
storeFlow.update { it.copy(enableService = false) } storeFlow.update { it.copy(enableService = false) }
} }
}
ManageService.autoStart()
onDestroyed {
A11yService.weakInstance = WeakReference(null) A11yService.weakInstance = WeakReference(null)
A11yService.isRunning.value = false A11yService.isRunning.value = false
} }

View File

@ -18,6 +18,7 @@ import li.songe.gkd.util.componentName
import li.songe.gkd.util.lastRestartA11yServiceTimeFlow import li.songe.gkd.util.lastRestartA11yServiceTimeFlow
import li.songe.gkd.util.storeFlow import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.toast import li.songe.gkd.util.toast
import li.songe.gkd.util.useLogLifecycle
class GkdTileService : TileService(), OnDestroy, OnChangeListen, OnTileClick { class GkdTileService : TileService(), OnDestroy, OnChangeListen, OnTileClick {
override fun onStartListening() { override fun onStartListening() {
@ -50,6 +51,7 @@ class GkdTileService : TileService(), OnDestroy, OnChangeListen, OnTileClick {
} }
init { init {
useLogLifecycle()
scope.launch { scope.launch {
combine( combine(
A11yService.isRunning, A11yService.isRunning,

View File

@ -2,8 +2,8 @@ package li.songe.gkd.service
import android.app.Service import android.app.Service
import android.content.Intent import android.content.Intent
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
@ -14,45 +14,32 @@ import li.songe.gkd.app
import li.songe.gkd.notif.abNotif import li.songe.gkd.notif.abNotif
import li.songe.gkd.notif.notifyService import li.songe.gkd.notif.notifyService
import li.songe.gkd.permission.notificationState import li.songe.gkd.permission.notificationState
import li.songe.gkd.util.OnCreate
import li.songe.gkd.util.OnDestroy
import li.songe.gkd.util.actionCountFlow import li.songe.gkd.util.actionCountFlow
import li.songe.gkd.util.getSubsStatus import li.songe.gkd.util.getSubsStatus
import li.songe.gkd.util.ruleSummaryFlow import li.songe.gkd.util.ruleSummaryFlow
import li.songe.gkd.util.storeFlow import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.useAliveFlow
class ManageService : Service() { class ManageService : Service(), OnCreate, OnDestroy {
override fun onBind(intent: Intent?) = null override fun onBind(intent: Intent?) = null
val scope = CoroutineScope(Dispatchers.Default)
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
isRunning.value = true onCreated()
abNotif.notifyService(this)
scope.launch {
combine(
A11yService.isRunning,
storeFlow,
ruleSummaryFlow,
actionCountFlow,
) { abRunning, store, ruleSummary, count ->
if (!abRunning) return@combine "无障碍未授权"
if (!store.enableMatch) return@combine "暂停规则匹配"
if (store.useCustomNotifText) {
return@combine store.customNotifText
.replace("\${i}", ruleSummary.globalGroups.size.toString())
.replace("\${k}", ruleSummary.appSize.toString())
.replace("\${u}", ruleSummary.appGroupSize.toString())
.replace("\${n}", count.toString())
}
return@combine getSubsStatus(ruleSummary, count)
}.debounce(500L).stateIn(scope, SharingStarted.Eagerly, "").collect { text ->
abNotif.copy(text = text).notifyService(this@ManageService)
}
}
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
isRunning.value = false onDestroyed()
}
val scope = MainScope().apply { onDestroyed { cancel() } }
init {
useAliveFlow(isRunning)
useNotif()
} }
companion object { companion object {
@ -79,3 +66,29 @@ class ManageService : Service() {
} }
} }
private fun ManageService.useNotif() {
onCreated {
abNotif.notifyService(this)
scope.launch {
combine(
A11yService.isRunning,
storeFlow,
ruleSummaryFlow,
actionCountFlow,
) { abRunning, store, ruleSummary, count ->
if (!abRunning) return@combine "无障碍未授权"
if (!store.enableMatch) return@combine "暂停规则匹配"
if (store.useCustomNotifText) {
return@combine store.customNotifText
.replace("\${i}", ruleSummary.globalGroups.size.toString())
.replace("\${k}", ruleSummary.appSize.toString())
.replace("\${u}", ruleSummary.appGroupSize.toString())
.replace("\${n}", count.toString())
}
return@combine getSubsStatus(ruleSummary, count)
}.debounce(500L).stateIn(scope, SharingStarted.Eagerly, "").collect { text ->
abNotif.copy(text = text).notifyService(this@useNotif)
}
}
}
}

View File

@ -2,6 +2,8 @@ package li.songe.gkd.util
import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityEvent
import com.blankj.utilcode.util.LogUtils
import kotlinx.coroutines.flow.MutableStateFlow
import java.util.WeakHashMap import java.util.WeakHashMap
private val callbacksMap by lazy { WeakHashMap<Any, HashMap<Int, MutableList<Any>>>() } private val callbacksMap by lazy { WeakHashMap<Any, HashMap<Int, MutableList<Any>>>() }
@ -15,13 +17,13 @@ private fun <T> Any.getCallbacks(method: Int): MutableList<T> {
interface CanOnCallback interface CanOnCallback
interface OnCreate : CanOnCallback { interface OnCreate : CanOnCallback {
fun onBeforeCreate(f: () -> Unit) { // fun onBeforeCreate(f: () -> Unit) {
getCallbacks<() -> Unit>(1).add(f) // getCallbacks<() -> Unit>(1).add(f)
} // }
//
fun onBeforeCreate() { // fun onBeforeCreate() {
getCallbacks<() -> Unit>(1).forEach { it() } // getCallbacks<() -> Unit>(1).forEach { it() }
} // }
fun onCreated(f: () -> Unit) { fun onCreated(f: () -> Unit) {
getCallbacks<() -> Unit>(2).add(f) getCallbacks<() -> Unit>(2).add(f)
@ -83,7 +85,7 @@ interface OnChangeListen : CanOnCallback {
} }
} }
interface OnTileClick { interface OnTileClick : CanOnCallback {
fun onTileClicked(f: () -> Unit) { fun onTileClicked(f: () -> Unit) {
getCallbacks<() -> Unit>(14).add(f) getCallbacks<() -> Unit>(14).add(f)
} }
@ -91,4 +93,33 @@ interface OnTileClick {
fun onTileClicked() { fun onTileClicked() {
getCallbacks<() -> Unit>(14).forEach { it() } getCallbacks<() -> Unit>(14).forEach { it() }
} }
}
fun CanOnCallback.useAliveFlow(stateFlow: MutableStateFlow<Boolean>) {
if (this is OnCreate) {
onCreated { stateFlow.value = true }
}
if (this is OnDestroy) {
onDestroyed { stateFlow.value = false }
}
}
fun CanOnCallback.useLogLifecycle() {
LogUtils.d("useLogLifecycle", this)
if (this is OnCreate) {
onCreated { LogUtils.d("onCreated", this) }
}
if (this is OnDestroy) {
onDestroyed { LogUtils.d("onDestroyed", this) }
}
if (this is OnA11yConnected) {
onA11yConnected { LogUtils.d("onA11yConnected", this) }
}
if (this is OnChangeListen) {
onStartListened { LogUtils.d("onStartListened", this) }
onStopListened { LogUtils.d("onStopListened", this) }
}
if (this is OnTileClick) {
onTileClicked { LogUtils.d("onTileClicked", this) }
}
} }

View File

@ -1,6 +1,5 @@
package li.songe.gkd.util package li.songe.gkd.util
import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.app.Activity.RESULT_OK import android.app.Activity.RESULT_OK
import android.content.Context import android.content.Context
@ -22,8 +21,10 @@ import kotlin.coroutines.suspendCoroutine
// https://github.com/npes87184/ScreenShareTile/blob/master/app/src/main/java/com/npes87184/screenshottile/ScreenshotService.kt // https://github.com/npes87184/ScreenShareTile/blob/master/app/src/main/java/com/npes87184/screenshottile/ScreenshotService.kt
@SuppressLint("WrongConstant") class ScreenshotUtil(
class ScreenshotUtil(private val context: Context, private val screenshotIntent: Intent) { private val context: Context,
private val screenshotIntent: Intent
) {
private val handler by lazy { Handler(Looper.getMainLooper()) } private val handler by lazy { Handler(Looper.getMainLooper()) }
private var virtualDisplay: VirtualDisplay? = null private var virtualDisplay: VirtualDisplay? = null
@ -37,6 +38,13 @@ class ScreenshotUtil(private val context: Context, private val screenshotIntent:
) as MediaProjectionManager ) as MediaProjectionManager
} }
private val width: Int
get() = ScreenUtils.getScreenWidth()
private val height: Int
get() = ScreenUtils.getScreenHeight()
private val dpi: Int
get() = ScreenUtils.getScreenDensityDpi()
fun destroy() { fun destroy() {
imageReader?.setOnImageAvailableListener(null, null) imageReader?.setOnImageAvailableListener(null, null)
virtualDisplay?.release() virtualDisplay?.release()
@ -101,10 +109,4 @@ class ScreenshotUtil(private val context: Context, private val screenshotIntent:
} }
}, handler) }, handler)
} }
companion object {
private val width by lazy { ScreenUtils.getScreenWidth() }
private val height by lazy { ScreenUtils.getScreenHeight() }
private val dpi by lazy { ScreenUtils.getScreenDensityDpi() }
}
} }