Tencent / tinker

Tinker is a hot-fix solution library for Android, it supports dex, library and resources update without reinstall apk.
Other
17.1k stars 3.33k forks source link

AndroidManifest清单中application的name属性自定义Application无法被自动添加到 loader 问题。 #1648

Open yanbober opened 2 years ago

yanbober commented 2 years ago
异常类型:编译异常

手机型号:无

手机系统版本:无

tinker版本:1.9.14.20

gradle版本:Gradle 7.4.2 / AGP 7.1.2

是否使用热更新SDK: 无

系统:win11-64、ubuntu

堆栈/日志:
TinkerManifestTask#readManifestApplicationName 方法在如上环境中运行时获取到清单文件中 application 的 name 值是空。

问题本质原因分析如下:

static String readManifestApplicationName(def project, String manifestPath) {
    def isr = null
    try {
        isr = new InputStreamReader(new FileInputStream(manifestPath), "utf-8")
        def xml = new XmlParser().parse(isr)
        def ns = new Namespace("http://schemas.android.com/apk/res/android", "android")

        def application = xml.application[0]

        def applicationName = null
        if (application) {
            applicationName = application.attributes()[ns.name]
            application.attributes().each { k, v->
                project.logger.error("FROM XmlParser: ${k.getClass()}---${k.namespaceURI}, ${k.localPart}")
                project.logger.error("FROM Namespace: ${ns.name.getClass()}---${ns.name.namespaceURI}, ${ns.name.localPart}")
                if (k.namespaceURI.equals(ns.name.namespaceURI) && k.localPart.equals(ns.name.localPart)) {
                    project.logger.error("=============matched 1")
                }
                if (k.equals(ns.name)) {
                    project.logger.error("=============matched 2")
                }
            }

        }
        return applicationName
    } finally {
        IOHelper.closeQuietly(isr)
    }
}

如上代码在高版本 gradle 配合 agp 中输出:

FROM XmlParser: class groovy.xml.QName---http://schemas.android.com/apk/res/android, name
FROM Namespace: class groovy.namespace.QName---http://schemas.android.com/apk/res/android, name
=============matched 1

低版本中没问题,分析发现,高低版本 gradle 依赖的 groovy 版本不一样。

低版本: groovy.util.XmlParser 内部对应 groovy.xml.QName groovy.xml.Namespace 内部对应 groovy.xml.QName 所以从 application.attributes() 的 map 中通过 QName key 去获取是可以找到对应 hashkey 的 value。

高版本: groovy.util.XmlParser 内部对应 groovy.xml.QName groovy.xml.Namespace 内部对应 groovy.namespace.QName 所以从 application.attributes() 的 map 中通过 QName key 去获取是无法找到对应 hashkey 的 value,因为类名 QName 相同,但是 package name 已经不同了,导致 equals 方法无法相等,除非 XmlParser 也使用 groovy.xml 包下的,但是高版本 groovy 才有这个。

因此修复方案换成 XmlSlurper 即可兼容高低不同版本 groovy。