zoukankan      html  css  js  c++  java
  • Android 开发进程 0.35 升级编译版本Android12

    Android12升级

    工作需要升级到编译版本31 在这里记录一下遇到的问题。
    错误:Manifest merger failedManifest merger failed 这个问题通常搜到的答案是manifest文件格式错误,但这是由于升级编译版本的原因,在Android SDK 31中,需要明确声明组件的 exported属性 android:exported="true" 官方文档如下:

    这意味这我们的manifest中每个组件的字段都需要添加exported的属性,包括所有依赖的库和module,主工程和module可以自己修改,但如果依赖的库没有写规范,结果会编译成功后在Android12版本上不能正确安装,而远程依赖的库和插件是不好修改的。此时要用到Android构建特性,Android在打包过程中,所有的组件和依赖产生的manifest文件将集合到一起,同时主manifest即我们app的manifest文件将会被重写,所以可以用gradle文件Groovy脚本实现。
    具体在GitHub上有实现,笔者在此只是转载:https://github.com/phamtdat/AndroidSnippets/blob/master/Android12AndroidManifestAddMissingAndroidExported/build.gradle
    具体代码如下:

    import org.w3c.dom.Element
    import org.w3c.dom.Node
    
    import javax.xml.transform.dom.DOMSource
    import javax.xml.transform.stream.StreamResult
    import javax.xml.transform.TransformerFactory
    import javax.xml.transform.Transformer
    
    def addAndroidExportedIfNecessary(File manifestFile) {
        def manifestAltered = false
        def reader = manifestFile.newReader()
        def document = groovy.xml.DOMBuilder.parse(reader)
        def application = document.getElementsByTagName("application").item(0)
        if (application != null) {
            println "Searching for activities, services and receivers with intent filters..."
            application.childNodes.each { child ->
                def childNodeName = child.nodeName
                if (childNodeName == "activity" || childNodeName == "activity-alias" ||
                        childNodeName == "service" || childNodeName == "receiver") {
                    def attributes = child.getAttributes()
                    if (attributes.getNamedItem("android:exported") == null) {
                        def intentFilters = child.childNodes.findAll {
                            it.nodeName == "intent-filter"
                        }
                        if (intentFilters.size() > 0) {
                            println "found ${childNodeName} ${attributes.getNamedItem("android:name").nodeValue} " +
                                    "with intent filters but without android:exported attribute"
    
                            def exportedAttrAdded = false
                            for (def i = 0; i < intentFilters.size(); i++) {
                                def intentFilter = intentFilters[i]
                                def actions = intentFilter.childNodes.findAll {
                                    it.nodeName == "action"
                                }
                                for (def j = 0; j < actions.size(); j++) {
                                    def action = actions[j]
                                    def actionName = action.getAttributes().getNamedItem("android:name").nodeValue
                                    if (actionName == "com.google.firebase.MESSAGING_EVENT") {
                                        println "adding exported=false to ${attributes.getNamedItem("android:name")}..."
                                        ((Element) child).setAttribute("android:exported", "false")
                                        manifestAltered = true
                                        exportedAttrAdded = true
                                    }
                                }
                            }
                            if (!exportedAttrAdded) {
                                println "adding exported=true to ${attributes.getNamedItem("android:name")}..."
                                ((Element) child).setAttribute("android:exported", "true")
                                manifestAltered = true
                            }
                        }
                    }
                }
            }
        }
        if (manifestAltered) {
            document.setXmlStandalone(true)
            Transformer transformer = TransformerFactory.newInstance().newTransformer()
            DOMSource source = new DOMSource(document)
            FileWriter writer = new FileWriter(manifestFile)
            StreamResult result = new StreamResult(writer)
            transformer.transform(source, result)
            println "Done adding missing android:exported attributes to your AndroidManifest.xml. You may want to" +
                    "additionally prettify it in Android Studio using [command + option + L](mac) or [CTRL+ALT+L](windows)."
        } else {
            println "Hooray, your AndroidManifest.xml did not need any change."
        }
    }
    
    def getMissingAndroidExportedComponents(File manifestFile) {
        List<Node> nodesFromDependencies = new ArrayList<>()
        def reader = manifestFile.newReader()
        def document = groovy.xml.DOMBuilder.parse(reader)
        def application = document.getElementsByTagName("application").item(0)
        if (application != null) {
            println "Searching for activities, services and receivers with intent filters..."
            application.childNodes.each { child ->
                def childNodeName = child.nodeName
                if (childNodeName == "activity" || childNodeName == "activity-alias" ||
                        childNodeName == "service" || childNodeName == "receiver") {
                    def attributes = child.getAttributes()
                    if (attributes.getNamedItem("android:exported") == null) {
                        def intentFilters = child.childNodes.findAll {
                            it.nodeName == "intent-filter"
                        }
                        if (intentFilters.size() > 0) {
                            println "found ${childNodeName} ${attributes.getNamedItem("android:name").nodeValue} " +
                                    "with intent filters but without android:exported attribute"
    
                            def exportedAttrAdded = false
                            for (def i = 0; i < intentFilters.size(); i++) {
                                def intentFilter = intentFilters[i]
                                def actions = intentFilter.childNodes.findAll {
                                    it.nodeName == "action"
                                }
                                for (def j = 0; j < actions.size(); j++) {
                                    def action = actions[j]
                                    def actionName = action.getAttributes().getNamedItem("android:name").nodeValue
                                    if (actionName == "com.google.firebase.MESSAGING_EVENT") {
                                        println "adding exported=false to ${attributes.getNamedItem("android:name")}..."
                                        ((Element) child).setAttribute("android:exported", "false")
                                        exportedAttrAdded = true
                                    }
                                }
                            }
                            if (!exportedAttrAdded) {
                                println "adding exported=true to ${attributes.getNamedItem("android:name")}..."
                                ((Element) child).setAttribute("android:exported", "true")
                            }
                            nodesFromDependencies.add(child)
                        }
                    }
                }
            }
        }
        return nodesFromDependencies
    }
    
    def addManifestFileComponents(File manifestFile, List<Node> components) {
        def reader = manifestFile.newReader()
        def document = groovy.xml.DOMBuilder.parse(reader)
        def application = document.getElementsByTagName("application").item(0)
        if (application != null) {
            println "Adding missing components with android:exported attribute to ${manifestFile.absolutePath} ..."
            components.each { node ->
                Node importedNode = document.importNode(node, true)
                application.appendChild(importedNode)
            }
        }
        if (components.size() > 0) {
            document.setXmlStandalone(true)
            Transformer transformer = TransformerFactory.newInstance().newTransformer()
            DOMSource source = new DOMSource(document)
            FileWriter writer = new FileWriter(manifestFile)
            StreamResult result = new StreamResult(writer)
            transformer.transform(source, result)
            println "Added missing app-dependencies components with android:exported attributes to your " +
                    "AndroidManifest.xml.You may want to additionally prettify it in Android Studio using " +
                    "[command + option + L](mac) or [CTRL+ALT+L](windows)."
        }
        println "----"
    }
    
    task doAddAndroidExportedIfNecessary {
        doLast {
            def root = new File(project.rootDir, "")
            if (root.isDirectory()) {
                def children = root.listFiles()
                for (def i = 0; i < children.size(); i++) {
                    File child = children[i]
                    if (child.isDirectory()) {
                        File srcDirectory = new File(child, "src")
                        if (srcDirectory.exists() && srcDirectory.isDirectory()) {
                            def srcChildren = srcDirectory.listFiles()
                            for (def j = 0; j < srcChildren.size(); j++) {
                                File manifestFile = new File(srcChildren[j], "AndroidManifest.xml")
                                if (manifestFile.exists() && manifestFile.isFile()) {
                                    println "found manifest file: ${manifestFile.absolutePath}"
                                    addAndroidExportedIfNecessary(manifestFile)
                                    println "-----"
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    
    task doAddAndroidExportedForDependencies {
        doLast {
            List<Node> missingComponents = new ArrayList<>()
            def root = new File(project.rootDir, "")
            if (root.isDirectory()) {
                def children = root.listFiles()
                for (def i = 0; i < children.size(); i++) {
                    File child = children[i]
                    if (child.isDirectory()) {
                        File mergedManifestsDirectory = new File(child, "build/intermediates/merged_manifests")
                        if (mergedManifestsDirectory.exists() && mergedManifestsDirectory.isDirectory()) {
                            def manifestFiles = mergedManifestsDirectory.listFiles().findAll { directoryChild ->
                                directoryChild.isDirectory() &&
                                        (new File(directoryChild, "AndroidManifest.xml")).exists()
                            }.stream().map { directoryWithManifest ->
                                new File(directoryWithManifest, "AndroidManifest.xml")
                            }.toArray()
    
                            if (manifestFiles.size() > 0) {
                                File mergedManifest = manifestFiles[0]
                                if (mergedManifest.exists() && mergedManifest.isFile()) {
                                    missingComponents = getMissingAndroidExportedComponents(mergedManifest)
    
                                    if (missingComponents.size() > 0) {
                                        File srcDirectory = new File(child, "src")
                                        if (srcDirectory.exists() && srcDirectory.isDirectory()) {
                                            def srcChildren = srcDirectory.listFiles()
                                            for (def j = 0; j < srcChildren.size(); j++) {
                                                File manifestFile = new File(srcChildren[j], "AndroidManifest.xml")
                                                if (manifestFile.exists() && manifestFile.isFile()) {
                                                    addManifestFileComponents(manifestFile, missingComponents)
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    

    具体使用方法将此代码加入app中的gradle中,也可以抽出为独立gradle文件,在build后执行doAddAndroidExportedIfNecessary 任务,如果成功后会在app中的manifest文件中找到遍历出来未明确表明的exported得组件声明。
    如果失败的话可以尝试将SDK版本降到30在执行构建任务再提高编译版本。之后就可以开启12的特新测试了。在此感谢提供脚本的大佬。

    吾生也有涯,而知也無涯。以有涯隨無涯,殆已
  • 相关阅读:
    题解 DTOJ #1438. 矮人排队(lineup)
    题解 DTOJ #4423. 「THUSC2019」塔
    题解 DTOJ #4123.「2019冬令营提高组」全连
    题解 DTOJ #4016.辉夜的夜空明珠(moon)
    题解 DTOJ #2498.大步小步(babystep)
    题解 DTOJ #3326.组队(group)
    题解 DTOJ #1515.三塔合一
    题解 DTOJ #2305.Bazarek
    【code】Splay 模板
    寻找乱序数组中第K大的数
  • 原文地址:https://www.cnblogs.com/baimiyishu/p/15365392.html
Copyright © 2011-2022 走看看