diff --git a/projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/classloaders/PluginClassLoader.kt b/projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/classloaders/PluginClassLoader.kt index 631f71a83..ea0e76948 100644 --- a/projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/classloaders/PluginClassLoader.kt +++ b/projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/classloaders/PluginClassLoader.kt @@ -20,6 +20,7 @@ package com.tencent.shadow.core.loader.classloaders import android.os.Build import dalvik.system.BaseDexClassLoader +import org.jetbrains.annotations.TestOnly import java.io.File @@ -48,18 +49,22 @@ class PluginClassLoader( * 宿主的白名单包名 * 在白名单包里面的宿主类,插件才可以访问 */ - private val allHostWhiteList: Array + private val allHostWhiteTrie = PackageNameTrie() private val loaderClassLoader = PluginClassLoader::class.java.classLoader!! init { - val defaultWhiteList = arrayOf( - "org.apache.commons.logging"//org.apache.commons.logging是非常特殊的的包,由系统放到App的PathClassLoader中. - ) - if (hostWhiteList != null) { - allHostWhiteList = defaultWhiteList.plus(hostWhiteList) - }else { - allHostWhiteList = defaultWhiteList + hostWhiteList?.forEach { + allHostWhiteTrie.insert(it) + } + + //org.apache.commons.logging是非常特殊的的包,由系统放到App的PathClassLoader中. + allHostWhiteTrie.insert("org.apache.commons.logging") + + //Android 9.0以下的系统里面带有http包,走系统的不走本地的 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + allHostWhiteTrie.insert("org.apache.http") + allHostWhiteTrie.insert("org.apache.http.**") } } @@ -79,11 +84,7 @@ class PluginClassLoader( } //包名在白名单中的类按双亲委派逻辑,从宿主中加载 - if (className.inPackage(allHostWhiteList) - //Android 9.0以下的系统里面带有http包,走系统的不走本地的 - || (Build.VERSION.SDK_INT < Build.VERSION_CODES.P - && className.startsWith("org.apache.http")) - ) { + if (className.inPackage(allHostWhiteTrie)) { return super.loadClass(className, resolve) } @@ -114,33 +115,75 @@ class PluginClassLoader( private fun String.subStringBeforeDot() = substringBeforeLast('.', "") +@Deprecated("use PackageNameTrie instead.") +@TestOnly internal fun String.inPackage(packageNames: Array): Boolean { + val trie = PackageNameTrie() + packageNames.forEach { + trie.insert(it) + } + return inPackage(trie) +} + +private fun String.inPackage(packageNames: PackageNameTrie): Boolean { val packageName = subStringBeforeDot() + return packageNames.isMatch(packageName) +} - return packageNames.any { - return@any when { - it == "" -> false - it == ".*" -> false - it == ".**" -> false - it.endsWith(".*") -> {//只允许一级子包 - val sub = packageName.subStringBeforeDot() - if (sub.isEmpty()) { - false - } else { - sub == it.subStringBeforeDot() - } +/** + * 基于Trie算法对包名进行前缀匹配 + */ +private class PackageNameTrie { + private class Node { + val subNodes = mutableMapOf() + var isLastPackageOfARule = false + } + + private val root = Node() + + fun insert(packageNameRule: String) { + var node = root + packageNameRule.split('.').forEach { + if (it.isEmpty()) return //"",".*",".**"这种无包名情况不允许设置 + + var subNode = node.subNodes[it] + if (subNode == null) { + subNode = Node() + node.subNodes[it] = subNode + } + node = subNode + } + node.isLastPackageOfARule = true + } + + fun isMatch(packageName: String): Boolean { + var node = root + + val split = packageName.split('.') + val lastIndex = split.size - 1 + for ((index, name) in split.withIndex()) { + // 只要下级包名规则中有**,就完成了匹配 + val twoStars = node.subNodes["**"] + if (twoStars != null) { + return true } - it.endsWith(".**") -> {//允许所有子包 - val sub = packageName.subStringBeforeDot() - if (sub.isEmpty()) { - false - } else { - "$sub.".startsWith(it.subStringBeforeDot() + '.') + + // 剩最后一级包名时,如果规则是*则完成比配 + if (index == lastIndex) { + val oneStar = node.subNodes["*"] + if (oneStar != null) { + return true } } - else -> packageName == it + + // 找不到下级包名时即匹配失败 + val subNode = node.subNodes[name] + if (subNode == null) { + return false + } else { + node = subNode + } } + return node.isLastPackageOfARule } } - -