android12适配名单_vmos适配安卓11

android12适配名单_vmos适配安卓11众所周知,从Android12开始,使用了TargetSDK31之后,四大组件如果使用了intent-filter,但是没显性质配置exportedApp将会无法安装,甚至编译不通过

android12适配名单_vmos适配安卓11"

众所周知,从 Android 12 开始,使用了 TargetSDK 31 之后,四大组件如果使用了 intent-filter,但是没显性质配置 exported App 将会无法安装,甚至编译不通过。

比如启动的 Activity 就需要设置 ex‍ported 为 true,至于其他组件是否设置为 true 则看它是否需要被其它应用调用。

然而这个事情的状态是这样的: 

  • 如果出现问题的 AndroidManifest 文件是您本地的,那手动修改即可;

  • 但如果出现问题的是第三方远程依赖,并且对方并没有提供源码和更新,您就无法直接修改;

  • 如果第三方依赖太多,查找哪些出了问题十分费时费力。

这块网上各个大佬之前都是采取脚本的方式解决的,原理其实也可简单,就是在打包过程中检索所有没有设置 exported 的组件,给他们动态配置上 exported,这里有个特殊需要注意的是,因为启动 Activity 默认就是需要被 Launcher 打开的,所以 "android.intent.action.MAIN" 需要 exported 设置为 true 。

脚本如下:

以下脚本经过测试最高可到支持的版本: 「gradle:4.0.0 & gradle-6.1.1-all.zip」

/**
 * 修改 Android 12 因为 exported 的构建问题
 */
android.applicationVariants.all { variant ->
    variant.outputs.all { output ->
        output.processResources.doFirst { pm ->
            String manifestPath = output.processResources.manifestFile
            def manifestFile = new File(manifestPath)
            def xml = new XmlParser(false, true).parse(manifestFile)
            def exportedTag = "android:exported"
            ///指定 space
            def androidSpace = new groovy.xml.Namespace('http://schemas.android.com/apk/res/android', 'android')

            def nodes = xml.application[0].'*'.findAll {
                //挑选要修改的节点,没有指定的 exported 的才需要增加
                (it.name() == 'activity' || it.name() == 'receiver' || it.name() == 'service') && it.attribute(androidSpace.exported) == null

            }
            ///添加 exported,默认 false
            nodes.each {
                def isMain = false
                it.each {
                    if (it.name() == "intent-filter") {
                        it.each {
                            if (it.name() == "action") {
                                if (it.attributes().get(androidSpace.name) == "android.intent.action.MAIN") {
                                    isMain = true
                                    println("......................MAIN FOUND......................")
                                }
                            }
                        }
                    }
                }
                it.attributes().put(exportedTag, "${isMain}")
            }

            PrintWriter pw = new PrintWriter(manifestFile)
            pw.write(groovy.xml.XmlUtil.serialize(xml))
            pw.close()
        }
    }

}

com.android.tools.build:gradle:4.0.0 以上版本

以下脚本经过测试支持的版本: 「gradle:4.1.0 & gradle-6.5.1-all.zip」

/**
 * 修改 Android 12 因为 exported 的构建问题
 */

android.applicationVariants.all { variant ->
    variant.outputs.each { output ->
        def processManifest = output.getProcessManifestProvider().get()
        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()) {
                def manifestFile = manifestOutFile
                ///这里第二个参数是 false ,所以 namespace 是展开的,所以下面不能用 androidSpace,而是用 nameTag
                def xml = new XmlParser(false, false).parse(manifestFile)
                def exportedTag = "android:exported"
                def nameTag = "android:name"
                ///指定 space
                //def androidSpace = new groovy.xml.Namespace('http://schemas.android.com/apk/res/android', 'android')

                def nodes = xml.application[0].'*'.findAll {
                    //挑选要修改的节点,没有指定的 exported 的才需要增加
                    //如果 exportedTag 拿不到可以尝试 it.attribute(androidSpace.exported)
                    (it.name() == 'activity' || it.name() == 'receiver' || it.name() == 'service') && it.attribute(exportedTag) == null

                }
                ///添加 exported,默认 false
                nodes.each {
                    def isMain = false
                    it.each {
                        if (it.name() == "intent-filter") {
                            it.each {
                                if (it.name() == "action") {
                                    //如果 nameTag 拿不到可以尝试 it.attribute(androidSpace.name)
                                    if (it.attributes().get(nameTag) == "android.intent.action.MAIN") {
                                        isMain = true
                                        println("......................MAIN FOUND......................")
                                    }
                                }
                            }
                        }
                    }
                    it.attributes().put(exportedTag, "${isMain}")
                }

                PrintWriter pw = new PrintWriter(manifestFile)
                pw.write(groovy.xml.XmlUtil.serialize(xml))
                pw.close()

            }

        }
    }
}

这段脚本你可以直接放到 app/build.gradle 下执行,也可以单独放到一个 gradle 文件之后 apply 引入

但是问题是到了gradle 4.2+、gradle-6.7.1-all.zip 的时候,这些脚本就不能用了,原因「从 gradle:4.2.0 & gradle-6.7.1-all.zip 开始,TargetSDK 31 下脚本会有异常,因为在 processDebugMainManifest (带有Main) 的阶段,会直接扫描依赖库的 AndroidManifest.xml 然后抛出直接报错,从而进不去 processDebugManifest 任务阶段就编译停止,所以实际上脚本并没有成功运行 ,针对这个,额,又有个大佬做了这块的处理,下面贴上大佬的解决方法:

manifest-exported-plugin

 大佬的插件叫这个名字,使用方法:

依赖方式

添加jitpack仓库

build.gradle

Gradle7.0 以下

buildscript {
	repositories {
			// ...
			maven { url 'https://jitpack.io' }
	}
}

Gradle7.0+,并且已经对依赖方式进行过调整,则可能需要添加到如下位置:settings.gradle

pluginManagement {
    repositories {
        //...
           maven { url 'https://jitpack.io' }
       }
 }

Gradle

dependencies {
      classpath 'com.github.xiachufang:manifest-exported-plugin:1.1.1'
}

使用方式

添加插件

在主app Model中添加:

apply plugin: 'com.xiachufang.manifest.exported'
或
plugins {
 id 'com.xiachufang.manifest.exported'
}

 build.gradle

apply plugin: 'com.xiachufang.manifest.exported'
...
  
exported {
    // 是否写入主Model
    enableMainManifest false
    // 规则
    ruleFile new File("$projectDir/xxx.json")
    // 输出文件,默认-app/build/exported/outManifestLog.md
	outPutFile null
}

配置参数说明

enableMainManifest

是否对主 model-AndroidManifest 进行修改

对于主model,属于业务可控的,建议开发者自行调整。

插件默认不会对主 model-AndroidManifest 进行修改,如果发现可用匹配上述规则的,即会进行修正。

开发者可根据日志中的提示,进行修改。

注意:这个操作会对Manifest的展示样式造成一定影响,建议一般不要打开。

outPutFile

日志输出目录,默认 app/build/exported/outManifest.md

ruleFile

具体的规则json文件, 格式如下:

{
  "actionRules": [
    "android.intent.action.MAIN"
  ],
  "whiteNames": [],
  "blackPackages": [],
  "blackNames": [],
  "blackIgnores": []
}

actionRules

默认判断规则,用于当前没有配置 exported 的修改逻辑,如果当前存在exported,则跳过。

具体判断逻辑:

如果 intent-filter – action 对应的 android:name 与 actionRules 中任意一条匹配,则将 exported 修改为 true ,否则为false 。

whiteNames

白名单类名,如果遇到此类,并且使用了 intent-filter ,则会将 exported 修改为 true

blackPackages

黑名单 包名合集,对于此包名下的类,如果使用了 intent-filter ,则会将 exported 直接修改为 false

blackNames

黑名单 类名合集,如果遇到此合集中的类,并且使用了 intent-filter ,则会将 exported 直接修改为 true 。判断逻辑会与上面 blackPackages 一起判断,两者满足其一即可。

blackIgnores

黑名单 下要忽视的类,在黑名单判断时,如果遇到此类,则使用默认规则 actionRules 判断。

注意的是:

对于主model下的 manifest ,默认不进行适配(可开关 enableManifest ),会通过日志进行输出,建议大家自行对比调整。

为什么默认不对主 model 进行适配?

  • 对于业务 model ,我们建议开发者自行适配,这属于我们可控范围,适配来说主要就是为了不可控的,即第三方 aar
  • 修改之后,会影响原有的 manifest 代码风格,需要重新格式化一下,相比默认的,增加了不少空格,暂时不知道怎么解决。

原理简述

在ProgressXXXMainManifest任务之前进行插入,通过对manifest进行修改,从而实现exported的适配。

通常默认情况下,会在build文件夹下生成个export文件夹,其中有个outManifestLog.md,这个就等于是个插件执行日志之类的东西,嗯,就这样,反正挺6的就是

参考文章:

Android 12 自动适配 exported 深入解析避坑 – 知乎

https://github.com/xiachufang/manifest-exported-plugin

今天的文章android12适配名单_vmos适配安卓11分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/89597.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注