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