性能优化

在 DexKit 中,多种查询或许能实现同样的功能,但是性能差距却可能相差几十倍。本节将介绍一些性能优化的技巧。

在 native 层,DexKit 会维护 Dex 中的类、方法以及字段的列表,那么在几个 API 中,DexKit 是如何扫描这些列表的呢?findClassfindMethodfindField 的遍历顺序均是按照各自列表的先后顺序进行遍历,然后再逐一对各个条件进行匹配。

declaredClass 条件过重

可能有些用户在使用 findMethodfindField 时,会使用 declaredClass 条件写出如下的查询:

private fun badCode(bridge: DexKitBridge) {
    bridge.findMethod {
        matcher {
            declaredClass {
                usingStrings("getUid", "", "_event")
            }
            modifiers = Modifier.PUBLIC or Modifier.STATIC
            returnType = "long"
            addInvoke {
                name = "parseLong"
            }
            addInvoke {
                name = "toString"
            }
        }
    }.single().let {
        println(it)
    }
}

这个搜索耗时 4310ms。 乍一看这个查询似乎没有什么问题,但是实际上这个查询的性能是非常差。为什么?前面提到过,findMethod API 会遍历一遍所有的方法,然后再逐一对各个条件进行匹配。而 method 与 class 之间却是一个多对一的关系,即一个 class 中可能包含多个 method,但是一个 method 只能属于一个 class。因此,遍历所有方法的过程中,每个 method 都会被匹配一次 declaredClass 条件,这就导致了性能的浪费。

那么,我们换一个思路,先搜索 declaredClass,配合链式调用就能在符合条件的类中再搜索 method,这样不就可以避免了吗?

private fun goodCode(bridge: DexKitBridge) {
    bridge.findClass {
        matcher {
            usingStrings("getUid", "", "_event")
        }
    }.findMethod {
        matcher {
            modifiers = Modifier.PUBLIC or Modifier.STATIC
            returnType = "long"
            addInvoke {
                name = "parseLong"
            }
            addInvoke {
                name = "toString"
            }
        }
    }.single().let {
        println(it)
    }
}

这个搜索耗时 77ms, 性能提升了数十倍之多。

在使用 findMethodfindField 时,尽量避免使用 declaredClass 附带过于复杂的逻辑。