perf: 优化appId和activityId状态获取

This commit is contained in:
lisonge 2023-09-15 14:08:32 +08:00
parent c48d984cd3
commit 6938e3b35e
4 changed files with 92 additions and 61 deletions

View File

@ -4,7 +4,7 @@ import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.ScreenUtils
import kotlinx.serialization.Serializable
import li.songe.gkd.service.GkdAbService
import li.songe.gkd.service.activityIdFlow
import li.songe.gkd.service.topActivityFlow
@Serializable
data class ComplexSnapshot(
@ -28,7 +28,7 @@ data class ComplexSnapshot(
fun createComplexSnapshot(): ComplexSnapshot {
val currentAbNode = GkdAbService.currentAbNode
val appId = currentAbNode?.packageName?.toString()
val currentActivityId = activityIdFlow.value
val currentActivityId = topActivityFlow.value?.activityId
val appInfo = if (appId == null) null else AppUtils.getAppInfo(appId)
return ComplexSnapshot(

View File

@ -4,8 +4,8 @@ import android.view.accessibility.AccessibilityNodeInfo
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.ToastUtils
import kotlinx.serialization.Serializable
import li.songe.gkd.service.activityIdFlow
import li.songe.gkd.service.forEachIndexed
import li.songe.gkd.service.topActivityFlow
import java.util.ArrayDeque
@Serializable
@ -45,7 +45,7 @@ data class NodeInfo(
// Failed to allocate a 245237304 byte allocation with 100663296 free bytes and 106MB until OOM
ToastUtils.showShort("节点数量至多保留50000,丢弃后续节点")
LogUtils.w(
nodeInfo.packageName, activityIdFlow.value, "节点数量过多"
nodeInfo.packageName, topActivityFlow.value?.activityId, "节点数量过多"
)
break
}

View File

@ -8,18 +8,27 @@ import li.songe.gkd.appScope
import li.songe.gkd.data.Rule
import li.songe.gkd.util.appIdToRulesFlow
val activityIdFlow by lazy {
MutableStateFlow<String?>(null)
data class TopActivity(
val appId: String,
val activityId: String? = null,
)
val topActivityFlow by lazy {
MutableStateFlow<TopActivity?>(null)
}
val appIdFlow by lazy {
MutableStateFlow<String?>(null)
}
//val activityIdFlow by lazy {
// MutableStateFlow<String?>(null)
//}
//
//val appIdFlow by lazy {
// MutableStateFlow<String?>(null)
//}
val currentRulesFlow by lazy {
combine(appIdToRulesFlow, appIdFlow, activityIdFlow) { appIdToRules, appId, activityId ->
(appIdToRules[appId] ?: emptyList()).filter { rule ->
rule.matchActivityId(activityId)
combine(appIdToRulesFlow, topActivityFlow) { appIdToRules, topActivity ->
(appIdToRules[topActivity?.appId] ?: emptyList()).filter { rule ->
rule.matchActivityId(topActivity?.activityId)
}
}.stateIn(appScope, SharingStarted.Eagerly, emptyList())
}

View File

@ -1,8 +1,9 @@
package li.songe.gkd.service
import android.content.ComponentName
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.os.Build
import android.util.Log
import android.view.Display
import android.view.KeyEvent
import android.view.accessibility.AccessibilityEvent
@ -15,8 +16,10 @@ import com.blankj.utilcode.util.ToastUtils
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import li.songe.gkd.composition.CompositionAbService
@ -42,6 +45,7 @@ import li.songe.selector.Selector
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@OptIn(FlowPreview::class)
class GkdAbService : CompositionAbService({
useLifeCycleLog()
@ -52,8 +56,7 @@ class GkdAbService : CompositionAbService({
service = context
onDestroy {
service = null
appIdFlow.value = null
activityIdFlow.value = null
topActivityFlow.value = null
}
ManageService.start(context)
@ -61,9 +64,32 @@ class GkdAbService : CompositionAbService({
ManageService.stop(context)
}
var isScreenLock = false
scope.launchWhile(IO) {
isScreenLock = ScreenUtils.isScreenLock()
delay(1000)
}
var okShizuku = false
scope.launchWhileTry(interval = 500) { // 根据 shizuku 获取 activityId, 准确
if (isScreenLock) return@launchWhileTry
if (shizukuIsSafeOK()) {
val topActivity =
activityTaskManager.getTasks(1, false, true)?.firstOrNull()?.topActivity
if (topActivity != null) {
topActivityFlow.value = TopActivity(topActivity.packageName, topActivity.className)
okShizuku = true
return@launchWhileTry
}
}
okShizuku = false
}
var lastKeyEventTime = -1L
onKeyEvent { event -> // 当按下音量键时捕获快照
if (storeFlow.value.captureVolumeKey && (event?.keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || event?.keyCode == KeyEvent.KEYCODE_VOLUME_UP)) {
val keyCode = event?.keyCode ?: return@onKeyEvent
if (storeFlow.value.captureVolumeKey && (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) {
val et = System.currentTimeMillis()
if (et - lastKeyEventTime > 3000) {
lastKeyEventTime = et
@ -81,47 +107,38 @@ class GkdAbService : CompositionAbService({
onServiceConnected { serviceConnected = true }
onInterrupt { serviceConnected = false }
onAccessibilityEvent { event -> // 根据事件获取 activityId, 概率不准确
val appId = safeActiveWindow?.packageName?.toString() ?: return@onAccessibilityEvent
appIdFlow.value = appId
when (event?.eventType) {
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, AccessibilityEvent.TYPE_WINDOWS_CHANGED -> {
val activityId = event.className?.toString() ?: return@onAccessibilityEvent
if (activityId == "com.miui.home.launcher.Launcher") { // 小米桌面 bug
if (appId != "com.miui.home") {
return@onAccessibilityEvent
}
}
fun isActivity(appId: String, activityId: String): Boolean {
return try {
packageManager.getActivityInfo(ComponentName(appId, activityId), 0)
} catch (e: PackageManager.NameNotFoundException) {
null
} != null
}
if (activityId.startsWith("android.") || activityId.startsWith("androidx.") || activityId.startsWith(
"com.android."
)
onAccessibilityEvent { event -> // 根据事件获取 activityId, 概率不准确
if (okShizuku || event == null) return@onAccessibilityEvent
when (event.eventType) {
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED -> {
val rightAppId = safeActiveWindow?.packageName ?: return@onAccessibilityEvent
val newAppId = event.packageName?.toString() ?: return@onAccessibilityEvent
val newActivityId = event.className?.toString() ?: return@onAccessibilityEvent
if (rightAppId.contentEquals(newAppId) && (newActivityId.startsWith(newAppId) || isActivity(
newAppId, newActivityId
))
) {
return@onAccessibilityEvent
topActivityFlow.value = TopActivity(newAppId, newActivityId)
} else {
topActivityFlow.value =
TopActivity(rightAppId.toString(), topActivityFlow.value?.activityId)
}
activityIdFlow.value = activityId
}
else -> {}
}
}
scope.launchWhileTry(interval = 500) { // 根据 shizuku 获取 activityId, 准确
if (shizukuIsSafeOK()) {
val topActivity =
activityTaskManager.getTasks(1, false, true)?.firstOrNull()?.topActivity
if (topActivity != null) {
appIdFlow.value = topActivity.packageName
activityIdFlow.value = topActivity.className
}
}
}
var isScreenLock = false
scope.launchWhile(IO) {
isScreenLock = ScreenUtils.isScreenLock()
delay(1000)
}
var lastToastTime = -1L
@ -157,8 +174,8 @@ class GkdAbService : CompositionAbService({
)
scope.launchTry(IO) {
val clickLog = ClickLog(
appId = appIdFlow.value,
activityId = activityIdFlow.value,
appId = topActivityFlow.value?.appId,
activityId = topActivityFlow.value?.activityId,
subsId = rule.subsItem.id,
groupKey = rule.group.key,
ruleIndex = rule.index,
@ -173,7 +190,6 @@ class GkdAbService : CompositionAbService({
}
scope.launchTry {
storeFlow.map { s -> s.updateSubsInterval }.collect { updateSubsInterval ->
if (updateSubsInterval <= 0) {
// clear
@ -187,7 +203,7 @@ class GkdAbService : CompositionAbService({
var lastUpdateSubsTime = System.currentTimeMillis()
scope.launchWhile(IO) { // 自动从网络更新订阅文件
delay(60_000)
if (!NetworkUtils.isAvailable() || storeFlow.value.updateSubsInterval <= 0) return@launchWhile
if (!NetworkUtils.isAvailable() || storeFlow.value.updateSubsInterval <= 0 || isScreenLock) return@launchWhile
if (System.currentTimeMillis() - lastUpdateSubsTime < storeFlow.value.updateSubsInterval.coerceAtLeast(
60 * 60_000
)
@ -198,7 +214,7 @@ class GkdAbService : CompositionAbService({
if (subsItem.updateUrl == null) return@forEach
try {
val newSubsRaw =
SubscriptionRaw.parse5(Singleton.client.get(subsItem.updateUrl).bodyAsText())
SubscriptionRaw.parse(Singleton.client.get(subsItem.updateUrl).bodyAsText())
if (newSubsRaw.id != subsItem.id) {
return@forEach
}
@ -225,14 +241,20 @@ class GkdAbService : CompositionAbService({
}
scope.launch {
combine(appIdFlow, activityIdFlow, currentRulesFlow) { appId, activityId, currentRules ->
appId to activityId to currentRules
}.collect {
val (appId, activityId) = it.first
val currentRules = it.second
Log.d("GkdAbService", "appId:$appId, activityId:$activityId")
if (currentRules.isNotEmpty()) {
LogUtils.d(*currentRules.map { r -> r.rule.matches }.toTypedArray())
combine(topActivityFlow, currentRulesFlow) { topActivity, currentRules ->
topActivity to currentRules
}.debounce(100).collect { (topActivity, currentRules) ->
if (storeFlow.value.enableService) {
LogUtils.d(
topActivity?.appId,
topActivity?.activityId,
*currentRules.map { r -> r.rule.matches }.toTypedArray()
)
} else {
LogUtils.d(
topActivity?.appId,
topActivity?.activityId,
)
}
}
}