Unity gradle dynamically modifies android:launchMode problem record

unity

Unity version Gradle version Android Gradle Plugin version
2023.1 7.3.3 7.2.1
2022.2 7.2 7.1.2
2022.1
2021.3
2021.2
2021.1 starting from 2021.1.16f1
2020.3 starting from 2020.3.15f1
6.1.1 4.0.1
2021.1 up to and including 2021.1.15f1
2020.1, 2020.2, 2020.3 up to and including 2020.3.14f1
5.6.4 4.0.1
2019.4 5.1.1 3.4.0

android:

plugin version Required Gradle version
1.0.0 - 1.1.3 2.2.1 - 2.3
1.2.0 - 1.3.1 2.2.1 - 2.9
1.5.0 2.2.1 - 2.13
2.0.0 - 2.1.2 2.10 - 2.13
2.1.3 - 2.2.3 2.14.1+
2.3.0+ 3.3+
3.0.0+ 4.1+
3.1.0+ 4.4+
3.2.0 - 3.2.1 4.6+
3.3.0 - 3.3.3 4.10.1+
3.4.0 - 3.4.3 5.1.1+
3.5.0 - 3.5.4 5.4.1+
3.6.0 - 3.6.4 5.6.4+
4.0.0+ 6.1.1+
4.1.0+ 6.5+
4.2.0+ 6.7.1+
7.0 7.0+
7.1 / 7.1.2 7.2+
7.2 7.3+
7.3 / 7.3.0 7.4+

Version found online:

android.applicationVariants.all { variant ->
    variant.outputs.all { output ->
        def processResources = output.hasProperty("processResourcesProvider") ?
                output.processResourcesProvider.get() : output.processResources
        processResources.doFirst { pm ->
            String manifestPath = processResources.manifestFile
            println "=====change manifestPath=====$manifestPath"
            def manifestContent = file(manifestPath).getText()

            def android = new groovy.xml.Namespace('http://schemas.android.com/apk/res/android', 'android')
            def xml = new XmlParser().parseText(manifestContent)
            def mainActivity = xml.application[0].activity.findAll() { ac ->
                def action = ac.depthFirst().findAll() { node ->
                    return node.attribute(android.name) == 'android.intent.action.MAIN'
                }
                return action.size() > 0;
            }
            println "=====found main activity=====${mainActivity.size()} $mainActivity"
            mainActivity[0].attributes().put(android.launchMode, 'singleTop')

            def serialize = groovy.xml.XmlUtil.serialize(xml)
            file(manifestPath).write(serialize)
        }
    }
}

Support apk and AAB versions, from android:LaunchMode. Why does Unity have it as singleTask? - Unity Forum

// Override LibraryManifest.xml values and switch launchMode to standard
android.applicationVariants.all { variant ->
    variant.outputs.each { output ->
        def processManifest = output.getProcessManifestProvider().get()
        processManifest.doLast { task ->
            def outputDir = task.getManifestOutputDirectory()
            File outputDirectory
            if (outputDir instanceof File) {
                outputDirectory = outputDir
            } else {
                outputDirectory = outputDir.get().asFile
            }
   
            File manifestOutFile = file("$outputDirectory/AndroidManifest.xml")
 
            if (manifestOutFile.exists() && manifestOutFile.canRead() && manifestOutFile.canWrite()) {
                def newManifest = manifestOutFile.getText().replace("android:launchMode=\"singleTask\"", "android:launchMode=\"standard\"")
                manifestOutFile.write(newManifest, 'UTF-8')
            }
   
            // Make sure to modify bundle_manifest as well
            outputDir = task.getBundleManifestOutputDirectory();
   
            if (outputDir instanceof File) {
                outputDirectory = outputDir
            } else {
                outputDirectory = outputDir.get().asFile
            }
                   
            manifestOutFile = file("$outputDirectory/AndroidManifest.xml")
 
            if (manifestOutFile.exists() && manifestOutFile.canRead() && manifestOutFile.canWrite()) {
                def bundleManifest = manifestOutFile.getText().replace("android:launchMode=\"singleTask\"", "android:launchMode=\"standard\"")
                manifestOutFile.write(bundleManifest, 'UTF-8')
            }
        }
    }
}

My optimized version is as follows (only supports before unity2022.2):

// Override LibraryManifest.xml values and switch launchMode to singleTop
android.applicationVariants.all { variant ->
    variant.outputs.each { output ->
        def processManifest = output.getProcessManifestProvider().get()
        processManifest.doLast { task ->
            def outputDir = task.getManifestOutputDirectory()
            println "=====change manifestPath==A===$outputDir"
            File outputDirectory
            if (outputDir instanceof File) {
                outputDirectory = outputDir
            } else {
                outputDirectory = outputDir.get().asFile
            }
   
            File manifestOutFile = file("$outputDirectory/AndroidManifest.xml")
            println "=====change manifestPath==B===$outputDirectory"
            if (manifestOutFile.exists() && manifestOutFile.canRead() && manifestOutFile.canWrite()) {
       /*          def newManifest = manifestOutFile.getText().replace("android:launchMode=\"singleTask\"", "android:launchMode=\"standard\"")
                manifestOutFile.write(newManifest, 'UTF-8') */
                 def manifestContent = file(manifestOutFile).getText()
                def android = new groovy.xml.Namespace('http://schemas.android.com/apk/res/android', 'android')
                def xml = new XmlParser().parseText(manifestContent)
                def mainActivity = xml.application[0].activity.findAll() { ac ->
                    def action = ac.depthFirst().findAll() { node ->
                        return node.attribute(android.name) == 'android.intent.action.MAIN'
                    }
                    return action.size() > 0;
                }
                println "=====found main activity=====${mainActivity.size()} $mainActivity"
                mainActivity[0].attributes().put(android.launchMode, 'singleTop')
    
                def serialize = groovy.xml.XmlUtil.serialize(xml)
                file(manifestOutFile).write(serialize)
            }
   
            // Make sure to modify bundle_manifest as well
            outputDir = task.getBundleManifestOutputDirectory();
   
            if (outputDir instanceof File) {
                outputDirectory = outputDir
            } else {
                outputDirectory = outputDir.get().asFile
            }
          println "=====change manifestPath==C===$outputDirectory"  
            manifestOutFile = file("$outputDirectory/AndroidManifest.xml")
             
            if (manifestOutFile.exists() && manifestOutFile.canRead() && manifestOutFile.canWrite()) {
      /*           def bundleManifest = manifestOutFile.getText().replace("android:launchMode=\"singleTask\"", "android:launchMode=\"standard\"")
                manifestOutFile.write(bundleManifest, 'UTF-8') */
                 def manifestContent = file(manifestOutFile).getText()
                def android = new groovy.xml.Namespace('http://schemas.android.com/apk/res/android', 'android')
                def xml = new XmlParser().parseText(manifestContent)
                def mainActivity = xml.application[0].activity.findAll() { ac ->
                    def action = ac.depthFirst().findAll() { node ->
                        return node.attribute(android.name) == 'android.intent.action.MAIN'
                    }
                    return action.size() > 0;
                }
                println "=====found main activity=====${mainActivity.size()} $mainActivity"
                mainActivity[0].attributes().put(android.launchMode, 'singleTop')
    
                def serialize = groovy.xml.XmlUtil.serialize(xml)
                file(manifestOutFile).write(serialize)
            }
        }
    }
}

Follow up:

Reference link:

Android 12 automatically adapts to exported, in-depth analysis of pit avoidance - Programmer Sought

Android 12 Automatically Adapts to Exported In-depth Analysis and Avoiding Pitfalls-Column-SoundNet RTE Developer Community

The above scripts are no longer effective in unity 2022.2. Refer to the above link to write different script combinations. After experimenting with three scripts in android studio (plugs 7.4.1) and unity2022, the most effective script is finally obtained.

Script 1 (unity is valid, android studio is invalid)

android.applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def vn
            if (variant.getFlavorName() != null && variant.getFlavorName() != "") {
                vn = variant.name;
            } else {
                if (variant.getBuildType().name == "release") {
                    vn = "Release"
                } else {
                    vn = "Debug"
                }
            }
            def taskName = "process${vn}MainManifest";
            try {
                println("=============== taskName ${taskName} ===============")
                project.getTasks().getByName(taskName)
            } catch (Exception e) {
                return
            }
            ///你的自定义名字
            project.getTasks().getByName(taskName).doFirst {
                it.getManifests().getFiles().each {
                    if (it.exists() && it.canRead() && it.canWrite()) {
                        def manifestFile = it
                        ///这里第二个参数是 false ,所以 namespace 是展开的,所以下面不能用 androidSpace,而是用 nameTag
                        def xml = new XmlParser(false, false).parse(manifestFile)
                        if (xml.application != null && xml.application.size() > 0) {
                            def mainActivity = xml.application[0].activity.findAll { ac ->
                                def action = ac.depthFirst().findAll() { node ->
                                    if(node != null){
                                        return node.attributes().get("android:name") == 'android.intent.action.MAIN'
                                    }
                                }

                                return action.size() > 0;
                            }
                            println "=====found main1 activity=====${mainActivity.size()} $mainActivity"
                            if(mainActivity[0]!=null){
                                mainActivity[0].attributes().put("android:launchMode", 'singleTop')
                            }
                            //mainActivity[0].attributes().put(android.launchMode, 'singleTop')
                            println "=====found main2 activity=====${mainActivity.size()} $mainActivity"
                            def serialize = groovy.xml.XmlUtil.serialize(xml)
                            file(it).write(serialize)
                        }
                    }
                }
            }
        }
    }

Script 2 (both android studio and UNITY are invalid)

android.applicationVariants.all { variant ->
        variant.outputs.each { output ->
            output.processResources.doLast {
                String manifestPath = output.processResources.manifestFile
                println "=====found manifestPath activity=====${manifestPath}"
                def manifestOutFile = new File(manifestPath)
                if (manifestOutFile.exists() && manifestOutFile.canRead() && manifestOutFile.canWrite()) {
                    ///这里第二个参数是 false ,所以 namespace 是展开的,所以下面不能用 androidSpace,而是用 nameTag
                    def xml = new XmlParser(false, false).parse(manifestOutFile)
                    def nameTag = "android:name"
                    println "=====found xml activity=====${xml}"
                    if (xml.application != null && xml.application.size() > 0) {
                        def mainActivity = xml.application[0].activity.findAll { ac ->
                            def action = ac.depthFirst().findAll() { node ->
                                if (node != null) {
                                    return node.attributes().get(nameTag) == 'android.intent.action.MAIN'
                                }
                            }

                            return action.size() > 0;
                        }
                        println "=====found main1 activity=====${mainActivity.size()} $mainActivity"
                        if (mainActivity[0] != null) {
                            mainActivity[0].attributes().put("android:launchMode", 'singleTop')
                        }
                        //mainActivity[0].attributes().put(android.launchMode, 'singleTop')
                        println "=====found main2 activity=====${mainActivity.size()} $mainActivity"
                        def serialize = groovy.xml.XmlUtil.serialize(xml)
                        file(manifestOutFile).write(serialize)
                    }
                }
            }
        }
    }

Script 3 (both android stuido unity are valid)

android.applicationVariants.all { variant ->
    variant.outputs.each { output ->
        def processManifest = output.getProcessManifestProvider().get()
        println("=============== processManifest ${processManifest} ===============")
        processManifest.doLast() { task ->
            def outputDir = task.multiApkManifestOutputDirectory
            File outputDirectory
            if (outputDir instanceof File) {
                outputDirectory = outputDir
            } else {
                outputDirectory = outputDir.get().asFile
            }
            File manifestOutFile = file("$outputDirectory/AndroidManifest.xml")
            println("----------- ${manifestOutFile} ----------- ")
            if (manifestOutFile.exists() && manifestOutFile.canRead() && manifestOutFile.canWrite()) {
                ///这里第二个参数是 false ,所以 namespace 是展开的,所以下面不能用 androidSpace,而是用 nameTag
                def xml = new XmlParser(false, false).parse(manifestOutFile)
                println "=====found xml activity=====${xml}"
                if (xml.application != null && xml.application.size() > 0) {
                    def mainActivity = xml.application[0].activity.findAll { ac ->
                        def action = ac.depthFirst().findAll() { node ->
                            if (node != null) {
                                return node.attributes().get('android:name') == 'android.intent.action.MAIN'
                            }
                        }

                        return action.size() > 0;
                    }
                    println "=====found main1 activity=====${mainActivity.size()} $mainActivity"
                    if (mainActivity[0] != null) {
                        mainActivity[0].attributes().put('android:launchMode', 'singleTop')
                    }
                    println "=====found main2 activity=====${mainActivity.size()} $mainActivity"
                    def serialize = groovy.xml.XmlUtil.serialize(xml)
                    file(manifestOutFile).write(serialize)
                }
            }

        }
    }
}

Guess you like

Origin blog.csdn.net/daweibalang717/article/details/125936568