mirror of
https://github.com/gkd-kit/gkd.git
synced 2024-11-16 11:42:22 +08:00
fix: cache node (#752)
This commit is contained in:
parent
db60d266fc
commit
cae215cab9
|
@ -26,6 +26,17 @@ private fun List<Any>.getInt(i: Int = 0) = get(i) as Int
|
|||
|
||||
private const val MAX_CACHE_SIZE = MAX_DESCENDANTS_SIZE
|
||||
|
||||
private val AccessibilityNodeInfo?.notExpiredNode: AccessibilityNodeInfo?
|
||||
get() {
|
||||
if (this != null) {
|
||||
val expiryMillis = if (text == null) 2000L else 1000L
|
||||
if (isExpired(expiryMillis)) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
class A11yContext(
|
||||
private val disableInterrupt: Boolean = false
|
||||
) {
|
||||
|
@ -35,6 +46,7 @@ class A11yContext(
|
|||
private var parentCache = LruCache<AccessibilityNodeInfo, AccessibilityNodeInfo>(MAX_CACHE_SIZE)
|
||||
var rootCache: AccessibilityNodeInfo? = null
|
||||
|
||||
private var lastClearTime = 0L
|
||||
private fun clearNodeCache(t: Long = System.currentTimeMillis()) {
|
||||
if (META.debuggable) {
|
||||
val sizeList = listOf(childCache.size(), parentCache.size(), indexCache.size())
|
||||
|
@ -59,18 +71,13 @@ class A11yContext(
|
|||
}
|
||||
}
|
||||
|
||||
private var lastClearTime = 0L
|
||||
private var lastAppChangeTime = appChangeTime
|
||||
private fun clearNodeCacheIfTimeout() {
|
||||
fun clearOldAppNodeCache() {
|
||||
if (appChangeTime != lastAppChangeTime) {
|
||||
lastAppChangeTime = appChangeTime
|
||||
clearNodeCache()
|
||||
return
|
||||
}
|
||||
val t = System.currentTimeMillis()
|
||||
if (t - lastClearTime > 30_000L) {
|
||||
clearNodeCache(t)
|
||||
}
|
||||
}
|
||||
|
||||
var currentRule: ResolvedRule? = null
|
||||
|
@ -84,6 +91,7 @@ class A11yContext(
|
|||
if (interruptInnerKey == interruptKey) return
|
||||
interruptInnerKey = interruptKey
|
||||
val rule = currentRule ?: return
|
||||
if (!activityRuleFlow.value.activePriority) return
|
||||
if (!activityRuleFlow.value.currentRules.any { it === rule }) return
|
||||
if (rule.isPriority()) return
|
||||
if (META.debuggable) {
|
||||
|
@ -99,12 +107,12 @@ class A11yContext(
|
|||
|
||||
private fun getA11Child(node: AccessibilityNodeInfo, index: Int): AccessibilityNodeInfo? {
|
||||
guardInterrupt()
|
||||
return node.getChild(index)
|
||||
return node.getChild(index)?.apply { setGeneratedTime() }
|
||||
}
|
||||
|
||||
private fun getA11Parent(node: AccessibilityNodeInfo): AccessibilityNodeInfo? {
|
||||
guardInterrupt()
|
||||
return node.parent
|
||||
return node.parent?.apply { setGeneratedTime() }
|
||||
}
|
||||
|
||||
private fun getA11ByText(
|
||||
|
@ -112,7 +120,9 @@ class A11yContext(
|
|||
value: String
|
||||
): List<AccessibilityNodeInfo> {
|
||||
guardInterrupt()
|
||||
return node.findAccessibilityNodeInfosByText(value)
|
||||
return node.findAccessibilityNodeInfosByText(value).apply {
|
||||
forEach { it.setGeneratedTime() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getA11ById(
|
||||
|
@ -120,7 +130,9 @@ class A11yContext(
|
|||
value: String
|
||||
): List<AccessibilityNodeInfo> {
|
||||
guardInterrupt()
|
||||
return node.findAccessibilityNodeInfosByViewId(value)
|
||||
return node.findAccessibilityNodeInfosByViewId(value).apply {
|
||||
forEach { it.setGeneratedTime() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFastQueryNodes(
|
||||
|
@ -135,20 +147,45 @@ class A11yContext(
|
|||
}
|
||||
|
||||
private fun getCacheRoot(node: AccessibilityNodeInfo? = null): AccessibilityNodeInfo? {
|
||||
if (rootCache == null) {
|
||||
if (rootCache.notExpiredNode == null) {
|
||||
rootCache = getA11Root()
|
||||
}
|
||||
if (node == rootCache) return null
|
||||
return rootCache
|
||||
}
|
||||
|
||||
private fun getCacheParent(node: AccessibilityNodeInfo): AccessibilityNodeInfo? {
|
||||
if (getCacheRoot() == node) {
|
||||
return null
|
||||
}
|
||||
parentCache[node].notExpiredNode?.let { return it }
|
||||
return getA11Parent(node).apply {
|
||||
if (this != null) {
|
||||
parentCache[node] = this
|
||||
} else {
|
||||
rootCache = node
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCacheChild(node: AccessibilityNodeInfo, index: Int): AccessibilityNodeInfo? {
|
||||
if (index !in 0 until node.childCount) {
|
||||
return null
|
||||
}
|
||||
return childCache[node to index].notExpiredNode ?: getA11Child(node, index)?.also { child ->
|
||||
indexCache[child] = index
|
||||
parentCache[child] = node
|
||||
childCache[node to index] = child
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPureIndex(node: AccessibilityNodeInfo): Int? {
|
||||
return indexCache[node]
|
||||
}
|
||||
|
||||
private fun getCacheIndex(node: AccessibilityNodeInfo): Int {
|
||||
indexCache[node]?.let { return it }
|
||||
getCacheParent(node)?.let(::getCacheChildren)?.forEachIndexed { index, child ->
|
||||
getCacheChildren(getCacheParent(node)).forEachIndexed { index, child ->
|
||||
if (child == node) {
|
||||
indexCache[node] = index
|
||||
return index
|
||||
|
@ -157,20 +194,6 @@ class A11yContext(
|
|||
return 0
|
||||
}
|
||||
|
||||
private fun getCacheParent(node: AccessibilityNodeInfo): AccessibilityNodeInfo? {
|
||||
if (rootCache == node) {
|
||||
return null
|
||||
}
|
||||
parentCache[node]?.let { return it }
|
||||
return getA11Parent(node).apply {
|
||||
if (this != null) {
|
||||
parentCache[node] = this
|
||||
} else {
|
||||
rootCache = node
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在无缓存时, 此方法小概率造成无限节点片段,底层原因未知
|
||||
*
|
||||
|
@ -191,18 +214,8 @@ class A11yContext(
|
|||
return depth
|
||||
}
|
||||
|
||||
private fun getCacheChild(node: AccessibilityNodeInfo, index: Int): AccessibilityNodeInfo? {
|
||||
if (index !in 0 until node.childCount) {
|
||||
return null
|
||||
}
|
||||
return childCache[node to index] ?: getA11Child(node, index)?.also { child ->
|
||||
indexCache[child] = index
|
||||
parentCache[child] = node
|
||||
childCache[node to index] = child
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCacheChildren(node: AccessibilityNodeInfo): Sequence<AccessibilityNodeInfo> {
|
||||
private fun getCacheChildren(node: AccessibilityNodeInfo?): Sequence<AccessibilityNodeInfo> {
|
||||
if (node == null) return emptySequence()
|
||||
return sequence {
|
||||
repeat(node.childCount.coerceAtMost(MAX_CHILD_SIZE)) { index ->
|
||||
val child = getCacheChild(node, index) ?: return@sequence
|
||||
|
@ -491,7 +504,6 @@ class A11yContext(
|
|||
rule: ResolvedRule,
|
||||
node: AccessibilityNodeInfo,
|
||||
): AccessibilityNodeInfo? {
|
||||
clearNodeCacheIfTimeout()
|
||||
currentRule = rule
|
||||
try {
|
||||
val queryNode = if (rule.matchRoot) {
|
||||
|
|
|
@ -255,6 +255,7 @@ private fun A11yService.useMatchRule() {
|
|||
}
|
||||
}
|
||||
var lastNodeUsed = false
|
||||
a11yContext.clearOldAppNodeCache()
|
||||
for (rule in activityRule.priorityRules) { // 规则数量有可能过多导致耗时过长
|
||||
if (delayRule != null && delayRule !== rule) continue
|
||||
val statusCode = rule.status
|
||||
|
|
|
@ -23,6 +23,8 @@ val AccessibilityService.safeActiveWindow: AccessibilityNodeInfo?
|
|||
// java.lang.SecurityException: Call from user 0 as user -2 without permission INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL not allowed.
|
||||
rootInActiveWindow.apply {
|
||||
a11yContext.rootCache = this
|
||||
}?.apply {
|
||||
setGeneratedTime()
|
||||
}
|
||||
// 在主线程调用会阻塞界面导致卡顿
|
||||
} catch (e: Exception) {
|
||||
|
@ -40,7 +42,9 @@ val AccessibilityEvent.safeSource: AccessibilityNodeInfo?
|
|||
} else {
|
||||
try {
|
||||
// 原因未知, 仍然报错 Cannot perform this action on a not sealed instance.
|
||||
source
|
||||
source?.apply {
|
||||
setGeneratedTime()
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
|
@ -64,6 +68,20 @@ fun AccessibilityNodeInfo.getVid(): CharSequence? {
|
|||
const val MAX_CHILD_SIZE = 512
|
||||
const val MAX_DESCENDANTS_SIZE = 4096
|
||||
|
||||
private const val A11Y_NODE_TIME_KEY = "generatedTime"
|
||||
fun AccessibilityNodeInfo.setGeneratedTime() {
|
||||
extras.putLong(A11Y_NODE_TIME_KEY, System.currentTimeMillis())
|
||||
}
|
||||
|
||||
fun AccessibilityNodeInfo.isExpired(expiryMillis: Long): Boolean {
|
||||
val generatedTime = extras.getLong(A11Y_NODE_TIME_KEY, -1)
|
||||
if (generatedTime == -1L) {
|
||||
setGeneratedTime()
|
||||
return false
|
||||
}
|
||||
return (System.currentTimeMillis() - generatedTime) > expiryMillis
|
||||
}
|
||||
|
||||
private val typeInfo by lazy { initDefaultTypeInfo().globalType }
|
||||
|
||||
fun Selector.checkSelector(): String? {
|
||||
|
|
Loading…
Reference in New Issue
Block a user