Skip to content

Commit

Permalink
perf: lru cache node
Browse files Browse the repository at this point in the history
  • Loading branch information
lisonge committed Jul 5, 2024
1 parent 2a16b33 commit 274f0e7
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 43 deletions.
75 changes: 43 additions & 32 deletions app/src/main/kotlin/li/songe/gkd/data/ResolvedRule.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package li.songe.gkd.data

import android.accessibilityservice.AccessibilityService
import android.util.Log
import android.view.accessibility.AccessibilityNodeInfo
import kotlinx.coroutines.Job
import li.songe.gkd.BuildConfig
import li.songe.gkd.service.GkdAbService
import li.songe.gkd.service.createCacheTransform
import li.songe.gkd.service.createNoCacheTransform
Expand Down Expand Up @@ -133,60 +135,59 @@ sealed class ResolvedRule(
else -> true
}

private val useCache = (matches + excludeMatches).any { s -> s.useCache }
private val useCache = (matches + excludeMatches + anyMatches).any { s -> s.useCache }
private val transform = if (useCache) defaultCacheTransform else defaultTransform

fun query(
node: AccessibilityNodeInfo?,
isRootNode: Boolean,
): AccessibilityNodeInfo? {
val t = System.currentTimeMillis()
if (t - lastCacheTime > MIN_CACHE_INTERVAL) {
clearNodeCache(t)
}
val nodeInfo = if (matchRoot) {
val rootNode = (if (isRootNode) {
node
} else {
GkdAbService.service?.safeActiveWindow
}) ?: return null
rootNode.apply {
transform.cache.parentMap[this] = null
transform.cache.rootNode = this
}
} else {
node
}
try {
if (nodeInfo == null) return null
var target: AccessibilityNodeInfo? = null
if (anyMatches.isNotEmpty()) {
for (selector in anyMatches) {
target = nodeInfo.querySelector(
selector,
quickFind,
transform.transform,
isRootNode || matchRoot
) ?: break
}
if (target == null) return null
}
for (selector in matches) {
if (nodeInfo == null) return null
var target: AccessibilityNodeInfo? = null
if (anyMatches.isNotEmpty()) {
for (selector in anyMatches) {
target = nodeInfo.querySelector(
selector,
quickFind,
transform.transform,
isRootNode || matchRoot
) ?: return null
}
for (selector in excludeMatches) {
nodeInfo.querySelector(
selector,
quickFind,
transform.transform,
isRootNode || matchRoot
)?.let { return null }
) ?: break
}
return target
} finally {
defaultTransform.cache.clear()
defaultCacheTransform.cache.clear()
if (target == null) return null
}
for (selector in matches) {
target = nodeInfo.querySelector(
selector,
quickFind,
transform.transform,
isRootNode || matchRoot
) ?: return null
}
for (selector in excludeMatches) {
nodeInfo.querySelector(
selector,
quickFind,
transform.transform,
isRootNode || matchRoot
)?.let { return null }
}
return target
}

private val performer = ActionPerformer.getAction(
Expand Down Expand Up @@ -275,5 +276,15 @@ fun getFixActivityIds(

private val defaultTransform by lazy { createNoCacheTransform() }
private val defaultCacheTransform by lazy { createCacheTransform() }


private var lastCacheTime = 0L
private const val MIN_CACHE_INTERVAL = 2000L

fun clearNodeCache(t: Long = System.currentTimeMillis()) {
lastCacheTime = t
if (BuildConfig.DEBUG) {
val sizeList = defaultCacheTransform.cache.sizeList
Log.d("cache", "clear cache, sizeList=$sizeList")
}
defaultTransform.cache.clear()
defaultCacheTransform.cache.clear()
}
42 changes: 31 additions & 11 deletions app/src/main/kotlin/li/songe/gkd/service/AbExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package li.songe.gkd.service

import android.accessibilityservice.AccessibilityService
import android.graphics.Rect
import android.util.LruCache
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import com.blankj.utilcode.util.LogUtils
Expand Down Expand Up @@ -108,6 +109,7 @@ fun AccessibilityNodeInfo.querySelector(


// https://github.com/gkd-kit/gkd/issues/115
// https://github.com/gkd-kit/gkd/issues/650
// 限制节点遍历的数量避免内存溢出
private const val MAX_CHILD_SIZE = 512
private const val MAX_DESCENDANTS_SIZE = 4096
Expand Down Expand Up @@ -209,17 +211,30 @@ data class CacheTransform(
val cache: NodeCache,
)

data class NodeCache(
val childMap: MutableMap<Pair<AccessibilityNodeInfo, Int>, AccessibilityNodeInfo> = HashMap(),
val indexMap: MutableMap<AccessibilityNodeInfo, Int> = HashMap(),
val parentMap: MutableMap<AccessibilityNodeInfo, AccessibilityNodeInfo?> = HashMap(),
) {

private operator fun <K, V> LruCache<K, V>.set(child: K, value: V): V {
return put(child, value)
}

private const val MAX_CACHE_SIZE = MAX_DESCENDANTS_SIZE

class NodeCache {
private val childMap =
LruCache<Pair<AccessibilityNodeInfo, Int>, AccessibilityNodeInfo>(MAX_CACHE_SIZE)
val indexMap = LruCache<AccessibilityNodeInfo, Int>(MAX_CACHE_SIZE)
private val parentMap = LruCache<AccessibilityNodeInfo, AccessibilityNodeInfo>(MAX_CACHE_SIZE)
var rootNode: AccessibilityNodeInfo? = null

fun clear() {
childMap.clear()
parentMap.clear()
indexMap.clear()
rootNode = null
childMap.evictAll()
parentMap.evictAll()
indexMap.evictAll()
}

val sizeList: List<Int>
get() = listOf(childMap.size(), parentMap.size(), indexMap.size())

fun getIndex(node: AccessibilityNodeInfo): Int {
indexMap[node]?.let { return it }
getParent(node)?.forEachIndexed { index, child ->
Expand All @@ -234,14 +249,19 @@ data class NodeCache(
}

fun getParent(node: AccessibilityNodeInfo): AccessibilityNodeInfo? {
if (rootNode == node) {
return null
}
val parent = parentMap[node]
if (parent != null) {
return parent
} else if (parentMap.contains(node)) {
return null
}
return node.parent.apply {
parentMap[node] = this
if (this != null) {
parentMap[node] = this
} else {
rootNode = node
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions app/src/main/kotlin/li/songe/gkd/service/GkdAbService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import li.songe.gkd.data.AttrInfo
import li.songe.gkd.data.GkdAction
import li.songe.gkd.data.RpcError
import li.songe.gkd.data.RuleStatus
import li.songe.gkd.data.clearNodeCache
import li.songe.gkd.debug.SnapshotExt
import li.songe.gkd.shizuku.getShizukuCanUsedFlow
import li.songe.gkd.shizuku.shizukuIsSafeOK
Expand Down Expand Up @@ -173,6 +174,10 @@ class GkdAbService : CompositionAbService({
pair
}
val activityRule = getAndUpdateCurrentRules()
if (activityRule.currentRules.isEmpty()) {
return@launchTry
}
clearNodeCache()
for (rule in (activityRule.currentRules)) { // 规则数量有可能过多导致耗时过长
val statusCode = rule.status
if (statusCode == RuleStatus.Status3 && rule.matchDelayJob == null) {
Expand Down

0 comments on commit 274f0e7

Please sign in to comment.