From 724ff77c97c6554618d386e0d567c28579484281 Mon Sep 17 00:00:00 2001 From: lisonge Date: Mon, 21 Oct 2024 00:04:41 +0800 Subject: [PATCH] perf: log lifecycle --- .../li/songe/gkd/debug/FloatingTileService.kt | 2 + .../kotlin/li/songe/gkd/debug/HttpService.kt | 78 +++++++++++-------- .../li/songe/gkd/debug/HttpTileService.kt | 2 + .../li/songe/gkd/debug/ScreenshotService.kt | 45 ++++++----- .../li/songe/gkd/service/A11yService.kt | 18 +++-- .../li/songe/gkd/service/GkdTileService.kt | 2 + .../li/songe/gkd/service/ManageService.kt | 69 +++++++++------- .../li/songe/gkd/util/LifecycleCallbacks.kt | 47 +++++++++-- .../li/songe/gkd/util/ScreenshotUtil.kt | 20 ++--- 9 files changed, 181 insertions(+), 102 deletions(-) diff --git a/app/src/main/kotlin/li/songe/gkd/debug/FloatingTileService.kt b/app/src/main/kotlin/li/songe/gkd/debug/FloatingTileService.kt index 47590b0..27082bc 100644 --- a/app/src/main/kotlin/li/songe/gkd/debug/FloatingTileService.kt +++ b/app/src/main/kotlin/li/songe/gkd/debug/FloatingTileService.kt @@ -10,6 +10,7 @@ import kotlinx.coroutines.launch import li.songe.gkd.util.OnChangeListen import li.songe.gkd.util.OnDestroy import li.songe.gkd.util.OnTileClick +import li.songe.gkd.util.useLogLifecycle class FloatingTileService : TileService(), OnDestroy, OnChangeListen, OnTileClick { override fun onStartListening() { @@ -41,6 +42,7 @@ class FloatingTileService : TileService(), OnDestroy, OnChangeListen, OnTileClic } init { + useLogLifecycle() scope.launch { combine( FloatingService.isRunning, diff --git a/app/src/main/kotlin/li/songe/gkd/debug/HttpService.kt b/app/src/main/kotlin/li/songe/gkd/debug/HttpService.kt index ccaa932..b7cdd5c 100644 --- a/app/src/main/kotlin/li/songe/gkd/debug/HttpService.kt +++ b/app/src/main/kotlin/li/songe/gkd/debug/HttpService.kt @@ -45,6 +45,8 @@ import li.songe.gkd.notif.notifyService import li.songe.gkd.permission.notificationState import li.songe.gkd.service.A11yService 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.getIpAddressInLocalNetwork 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.toast import li.songe.gkd.util.updateSubscription +import li.songe.gkd.util.useAliveFlow +import li.songe.gkd.util.useLogLifecycle import java.io.File -class HttpService : Service() { - private val scope = MainScope() +class HttpService : Service(), OnCreate, OnDestroy { + 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 var server: EmbeddedServer? = null - override fun onCreate() { - super.onCreate() - isRunning.value = true - localNetworkIpsFlow.value = getIpAddressInLocalNetwork() - scope.launchTry(Dispatchers.IO) { - httpServerPortFlow.collect { port -> - server?.stop() - server = try { - scope.createServer(port).apply { start() } - } catch (e: Exception) { - LogUtils.d("HTTP服务启动失败", e) - null - } - if (server == null) { - toast("HTTP服务启动失败,您可以尝试切换端口后重新启动") - stopSelf() - return@collect - } - httpNotif.copy(text = "HTTP服务-$port").notifyService(this@HttpService) + init { + useLogLifecycle() + useAliveFlow(isRunning) + + onCreated { localNetworkIpsFlow.value = getIpAddressInLocalNetwork() } + onDestroyed { localNetworkIpsFlow.value = emptyList() } + + onDestroyed { + if (storeFlow.value.autoClearMemorySubs) { + deleteSubscription(LOCAL_HTTP_SUBS_ID) } } - } - override fun onDestroy() { - super.onDestroy() - scope.cancel() - isRunning.value = false - localNetworkIpsFlow.value = emptyList() - if (storeFlow.value.autoClearMemorySubs) { - deleteSubscription(LOCAL_HTTP_SUBS_ID) + onCreated { + scope.launchTry(Dispatchers.IO) { + httpServerPortFlow.collect { port -> + server?.stop() + server = try { + scope.createServer(port).apply { start() } + } 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)) } } - - override fun onBind(intent: Intent?) = null } @Serializable diff --git a/app/src/main/kotlin/li/songe/gkd/debug/HttpTileService.kt b/app/src/main/kotlin/li/songe/gkd/debug/HttpTileService.kt index 015f87f..2e78730 100644 --- a/app/src/main/kotlin/li/songe/gkd/debug/HttpTileService.kt +++ b/app/src/main/kotlin/li/songe/gkd/debug/HttpTileService.kt @@ -10,6 +10,7 @@ import kotlinx.coroutines.launch import li.songe.gkd.util.OnChangeListen import li.songe.gkd.util.OnDestroy import li.songe.gkd.util.OnTileClick +import li.songe.gkd.util.useLogLifecycle class HttpTileService : TileService(), OnDestroy, OnChangeListen, OnTileClick { override fun onStartListening() { @@ -41,6 +42,7 @@ class HttpTileService : TileService(), OnDestroy, OnChangeListen, OnTileClick { } init { + useLogLifecycle() scope.launch { combine( HttpService.isRunning, diff --git a/app/src/main/kotlin/li/songe/gkd/debug/ScreenshotService.kt b/app/src/main/kotlin/li/songe/gkd/debug/ScreenshotService.kt index a0d4298..bb4cab1 100644 --- a/app/src/main/kotlin/li/songe/gkd/debug/ScreenshotService.kt +++ b/app/src/main/kotlin/li/songe/gkd/debug/ScreenshotService.kt @@ -1,26 +1,29 @@ package li.songe.gkd.debug -import android.annotation.SuppressLint import android.app.Service -import android.content.Context import android.content.Intent import com.blankj.utilcode.util.LogUtils import kotlinx.coroutines.flow.MutableStateFlow import li.songe.gkd.app import li.songe.gkd.notif.notifyService 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.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 onCreate() { super.onCreate() - isRunning.value = true - screenshotNotif.notifyService(this) + onCreated() } + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { try { return super.onStartCommand(intent, flags, startId) @@ -35,25 +38,31 @@ class ScreenshotService : Service() { override fun onDestroy() { super.onDestroy() - isRunning.value = false - screenshotUtil?.destroy() - screenshotUtil = null + onDestroyed() + } + + 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 { - suspend fun screenshot() = screenshotUtil?.execute() - - @SuppressLint("StaticFieldLeak") - private var screenshotUtil: ScreenshotUtil? = null - - fun start(context: Context = app, intent: Intent) { + private var instance = WeakReference(null) + val isRunning = MutableStateFlow(false) + suspend fun screenshot() = instance.get()?.screenshotUtil?.execute() + fun start(intent: Intent) { intent.component = ScreenshotService::class.componentName - context.startForegroundService(intent) + app.startForegroundService(intent) } - val isRunning = MutableStateFlow(false) - fun stop(context: Context = app) { - context.stopService(Intent(context, ScreenshotService::class.java)) + fun stop() { + app.stopService(Intent(app, ScreenshotService::class.java)) } } } \ No newline at end of file diff --git a/app/src/main/kotlin/li/songe/gkd/service/A11yService.kt b/app/src/main/kotlin/li/songe/gkd/service/A11yService.kt index c26bad5..8657d16 100644 --- a/app/src/main/kotlin/li/songe/gkd/service/A11yService.kt +++ b/app/src/main/kotlin/li/songe/gkd/service/A11yService.kt @@ -56,6 +56,7 @@ import li.songe.gkd.util.map import li.songe.gkd.util.showActionToast import li.songe.gkd.util.storeFlow import li.songe.gkd.util.toast +import li.songe.gkd.util.useLogLifecycle import li.songe.selector.MatchOption import li.songe.selector.Selector import java.lang.ref.WeakReference @@ -89,6 +90,7 @@ class A11yService : AccessibilityService(), OnCreate, OnA11yConnected, OnA11yEve val scope = CoroutineScope(Dispatchers.Default).apply { onDestroyed { cancel() } } init { + useLogLifecycle() useRunningState() useAliveView() useCaptureVolume() @@ -485,19 +487,19 @@ private fun A11yService.useRuleChangedLog() { } private fun A11yService.useRunningState() { - A11yService.weakInstance = WeakReference(this) - A11yService.isRunning.value = true - if (!storeFlow.value.enableService) { - // https://github.com/gkd-kit/gkd/issues/754 - storeFlow.update { it.copy(enableService = true) } + onCreated { + A11yService.weakInstance = WeakReference(this) + A11yService.isRunning.value = true + if (!storeFlow.value.enableService) { + // https://github.com/gkd-kit/gkd/issues/754 + storeFlow.update { it.copy(enableService = true) } + } + ManageService.autoStart() } onDestroyed { if (storeFlow.value.enableService) { storeFlow.update { it.copy(enableService = false) } } - } - ManageService.autoStart() - onDestroyed { A11yService.weakInstance = WeakReference(null) A11yService.isRunning.value = false } diff --git a/app/src/main/kotlin/li/songe/gkd/service/GkdTileService.kt b/app/src/main/kotlin/li/songe/gkd/service/GkdTileService.kt index 008fd2e..e970a4f 100644 --- a/app/src/main/kotlin/li/songe/gkd/service/GkdTileService.kt +++ b/app/src/main/kotlin/li/songe/gkd/service/GkdTileService.kt @@ -18,6 +18,7 @@ import li.songe.gkd.util.componentName import li.songe.gkd.util.lastRestartA11yServiceTimeFlow import li.songe.gkd.util.storeFlow import li.songe.gkd.util.toast +import li.songe.gkd.util.useLogLifecycle class GkdTileService : TileService(), OnDestroy, OnChangeListen, OnTileClick { override fun onStartListening() { @@ -50,6 +51,7 @@ class GkdTileService : TileService(), OnDestroy, OnChangeListen, OnTileClick { } init { + useLogLifecycle() scope.launch { combine( A11yService.isRunning, diff --git a/app/src/main/kotlin/li/songe/gkd/service/ManageService.kt b/app/src/main/kotlin/li/songe/gkd/service/ManageService.kt index 883166d..5967e8c 100644 --- a/app/src/main/kotlin/li/songe/gkd/service/ManageService.kt +++ b/app/src/main/kotlin/li/songe/gkd/service/ManageService.kt @@ -2,8 +2,8 @@ package li.songe.gkd.service import android.app.Service import android.content.Intent -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted 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.notifyService 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.getSubsStatus import li.songe.gkd.util.ruleSummaryFlow 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 - val scope = CoroutineScope(Dispatchers.Default) override fun onCreate() { super.onCreate() - isRunning.value = true - 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) - } - } + onCreated() } override fun onDestroy() { super.onDestroy() - isRunning.value = false + onDestroyed() + } + + val scope = MainScope().apply { onDestroyed { cancel() } } + + init { + useAliveFlow(isRunning) + useNotif() } 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) + } + } + } +} diff --git a/app/src/main/kotlin/li/songe/gkd/util/LifecycleCallbacks.kt b/app/src/main/kotlin/li/songe/gkd/util/LifecycleCallbacks.kt index 4895eef..8de8f8e 100644 --- a/app/src/main/kotlin/li/songe/gkd/util/LifecycleCallbacks.kt +++ b/app/src/main/kotlin/li/songe/gkd/util/LifecycleCallbacks.kt @@ -2,6 +2,8 @@ package li.songe.gkd.util import android.view.accessibility.AccessibilityEvent +import com.blankj.utilcode.util.LogUtils +import kotlinx.coroutines.flow.MutableStateFlow import java.util.WeakHashMap private val callbacksMap by lazy { WeakHashMap>>() } @@ -15,13 +17,13 @@ private fun Any.getCallbacks(method: Int): MutableList { interface CanOnCallback interface OnCreate : CanOnCallback { - fun onBeforeCreate(f: () -> Unit) { - getCallbacks<() -> Unit>(1).add(f) - } - - fun onBeforeCreate() { - getCallbacks<() -> Unit>(1).forEach { it() } - } +// fun onBeforeCreate(f: () -> Unit) { +// getCallbacks<() -> Unit>(1).add(f) +// } +// +// fun onBeforeCreate() { +// getCallbacks<() -> Unit>(1).forEach { it() } +// } fun onCreated(f: () -> Unit) { getCallbacks<() -> Unit>(2).add(f) @@ -83,7 +85,7 @@ interface OnChangeListen : CanOnCallback { } } -interface OnTileClick { +interface OnTileClick : CanOnCallback { fun onTileClicked(f: () -> Unit) { getCallbacks<() -> Unit>(14).add(f) } @@ -91,4 +93,33 @@ interface OnTileClick { fun onTileClicked() { getCallbacks<() -> Unit>(14).forEach { it() } } +} + +fun CanOnCallback.useAliveFlow(stateFlow: MutableStateFlow) { + 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) } + } } \ No newline at end of file diff --git a/app/src/main/kotlin/li/songe/gkd/util/ScreenshotUtil.kt b/app/src/main/kotlin/li/songe/gkd/util/ScreenshotUtil.kt index 7029e91..f1332fe 100644 --- a/app/src/main/kotlin/li/songe/gkd/util/ScreenshotUtil.kt +++ b/app/src/main/kotlin/li/songe/gkd/util/ScreenshotUtil.kt @@ -1,6 +1,5 @@ package li.songe.gkd.util -import android.annotation.SuppressLint import android.app.Activity import android.app.Activity.RESULT_OK 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 -@SuppressLint("WrongConstant") -class ScreenshotUtil(private val context: Context, private val screenshotIntent: Intent) { +class ScreenshotUtil( + private val context: Context, + private val screenshotIntent: Intent +) { private val handler by lazy { Handler(Looper.getMainLooper()) } private var virtualDisplay: VirtualDisplay? = null @@ -37,6 +38,13 @@ class ScreenshotUtil(private val context: Context, private val screenshotIntent: ) 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() { imageReader?.setOnImageAvailableListener(null, null) virtualDisplay?.release() @@ -101,10 +109,4 @@ class ScreenshotUtil(private val context: Context, private val screenshotIntent: } }, handler) } - - companion object { - private val width by lazy { ScreenUtils.getScreenWidth() } - private val height by lazy { ScreenUtils.getScreenHeight() } - private val dpi by lazy { ScreenUtils.getScreenDensityDpi() } - } } \ No newline at end of file