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

android 错误 exported needs to be explicitly specified for Apps targeting Android 12 b、如果还报错误,那我们可以在build.gradle里面的dependencies里面, 采用二分大法, 先块注释一部分依赖引用(我们先不用去管代码里面由于没有引用,而引起的相关报红,只是暂时注释而已,找问题而已,问题找到后再恢复),再执行。通过以上两步后再次运行, 如果三方库的android:exported没问题,问题也就解决了,但如果还是爆错,那就是三方库没有解决这个问题导致的。,根据错误提示信息,可知道失败是四大组件的哪个组件,还可知道有几个失败的问题。如果不再报错误,则问题解决。 阅读详情

作者:恋猫de小郭

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

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

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

  • 如果出现问题的 AndroidManifest 文件是你本地的,那手动修改即可;
  • 但如果出现问题的是第三方远程依赖,并且对方并没有提供源码和更新,你就无法直接修改;
  • 如果第三方依赖太多,查找哪些出了问题十分费时费力。

脚本

所以在之前的 《Android 12 快速适配要点》 一文中提供了一套脚本,专门用于适配 Android 12 下缺少 android:exported 无法编译或者安装的问题,但是在这期间收到了不少问题反馈:

com.android.tools.build:gradle:4.0.0 以及其下版本

以下脚本经过测试最高可到支持的版本: 「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 引入,它的作用就是:

❝
在打包过程中检索所有没有设置 exported 的组件,给他们动态配置上 exported,这里有个特殊需要注意的是,因为启动 Activity 默认就是需要被 Launcher 打开的,所以 "android.intent.action.MAIN" 需要 exported 设置为 true 。(「PS:更正规应该是用 LAUNCHER 类别,这里故意用 MAIN」)
❞

而后综合问题,具体反馈的问题有 :

  • label直接写死中文,不是引用 @string 导致的在 3.x 的版本可以正常运行,但不能打包 ;

  • XmlParser 类找不到,这个首先确定 AGP 版本和 Gradle 版本是否匹配,具体可见 gradle-plugin,另外可以通过 「groovy.util.XmlParser」 或者 「groovy.xml.XmlParser」 全路径指定使用 ,如果是 gradle 文件里显示红色并不会影响运行;

  • 「运行报错提示 android:exported needs,这个就是今天需要输入聊的」;

Error: android:exported needs to be explicitly specified for <xxxx>. Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported` when the corresponding component has an intent filter defined.

基于上述脚本测试和反馈,目前的结论是:

❝
「从 gradle:4.2.0 & gradle-6.7.1-all.zip 开始,TargetSDK 31 下脚本会有异常,因为在 processDebugMainManifest (带有Main) 的阶段,会直接扫描依赖库的 AndroidManifest.xml 然后抛出直接报错,从而进不去 processDebugManifest 任务阶段就编译停止,所以实际上脚本并没有成功运行」。
❞

所以此时拿不到 mergerd_manifest 下的文件,因为 mergerd_manifest 下 AndroidManifest.xml 也还没创建成功,没办法进入 task ,也就是该脚本目前只能针对 gradle:4.1.0 以及其下版本安装 apk 到 Android12 的机器上, 有 intent-filter 但没有 exoprted 的适配问题,「基于这个问题,不知道各位是否有什么好的建议?」

新脚本

而目前基于这个问题,这里提供了如下脚本,在 gradle:4.2.0 & gradle-6.7.1-all.zip 以及 7.0 的版本上,「该脚本的作用是在运行时自动帮你打印出现问题的 aar 包依赖路径和组建名称」:

android.applicationVariants.all { variant ->
    variant.outputs.each { output ->
        //println("=============== ${variant.getBuildType().name.toUpperCase()} ===============")
        //println("=============== ${variant.getFlavorName()} ===============")
        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 {
            //def method = it.getClass().getMethods()
            it.getManifests().getFiles().each {
                if (it.exists() && it.canRead()) {
                    def manifestFile = it
                    def exportedTag = "android:exported"
                    def nameTag = "android:name"
                    ///这里第二个参数是 false ,所以 namespace 是展开的,所以下面不能用 androidSpace,而是用 nameTag
                    def xml = new XmlParser(false, false).parse(manifestFile)
                    if (xml.application != null && xml.application.size() > 0) {
                        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

                        }
                        if (nodes.application != null && nodes.application.size() > 0) {
                            nodes.each {
                                def t = it
                                it.each {
                                    if (it.name() == "intent-filter") {
                                        println("$manifestFile \n .....................${t.attributes().get(nameTag)}......................")
                                    }
                                }
                            }
                        }
                    }


                }
            }
        }
    }
}

如下图所示,「因为目前官方如红色信息内容其实指向并不正确,容易误导问题方向,所以通过上述脚本打印,可以快速查找到问题所在的点,然后通过 tool:replace 临时解决」。

具体为什么之前的脚本在高版本 AGP 下无法使用,原因在于新版本在 processDebugMainManifest ,或者说 processXXXXXXMainManifest 的处理逻辑发生了变化,通过找到 processDebugMainManifest 的实现类,可以看到问题出现就是在于 Merging library manifest 。

❝
processDebugMainManifest 的实现在 ProcessApplicationManifest 里,对应路径是 ProcessApplicationManifest -> MainfestHelper mergeManifestsForApplication -> MainfestMerger2
❞

错误是在 Merging library manifest的阶段出现异常,但是这个阶段的 task 里对于第三方依赖路径的输入,主要是从 private fun computeFullProviderList 方法开始,所以输入到 mergeManifestsForApplication 里的第三方路径是通过这个私有方法生成。

感觉唯一可以考虑操作的就是内部的 manifests 对象去变换路径,但是它是 private ,并且内部并不能很好复写其内容。

「另外因为 aar 文件里的 AndroidManifset 是 readOnly ,所以如果真的要修改,感觉只能在输入之前读取到对应 AndroidManifset, 并生成临时文件,在 manifests 对象中更改其路径来完成」,不知道大家有没有什么比较好的思路 。

❝
如果有好的解决办法,后续再更新。
❞

最后

最后再说一个坑 ,如果你是低版本 Gradle 可以打包成功,但是运行到 Android12 机器的时候,可能会因为没有 exported 遇到安装失败的问题:

1、如果是模拟器 12,你可能会看到如下所示的错误提示 ,提示上显示还是很直观的, 直接告诉你是 android:exported 的问题:

* What went wrong:
Execution failed for task ':app:installDebug'.
> java.util.concurrent.ExecutionException: com.android.builder.testing.api.DeviceException: com.android.ddmlib.InstallException: INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: Failed parse during installPackageLI: /data/app/vmdl487461761.tmp/base.apk (at Binary XML file line #358): xxxxx.Activity: Targeting S+ (version 31 and above) requires that an explicit value for android:exported be defined when intent filters are present

2、如果你是真机 12,那可能就是这样的提示,提示然是 INSTALL_FAILED_USER_RESTRICTED ,不得不说小米系统这个安装失败很具误导性,比如 minSDK 太高导致无法安装,在小米上也会是 INSTALL_FAILED_USER_RESTRICTED:

基本上内容就这些,具体如何进一步优化还待后续测试, 「所以针对脚本实现,你还有什么问题或者想法,欢迎评论交流 ~」

android12适配 前言:当我们的targetSdkVersion 31指定了最高支持android12时,若没有做文中所介绍的适配,则应用在android12设备上会安装失败(虽然debug时非android12手机可以编译)。 比如我们的上架小米应用市场时,自动依据检targetSdkVersion 测我们apk,若当我们指定31时,没做视频的情况下,通常小米应用市场会上架审核不通过,如: 1、使用adb install命令连续三次无法安装成功。请自行使用adb命令对andorid版本:12进行测试。 并不是 阅读详情

相关推荐

Android 12(API 31)新特性及适配指南

Android 12(API 31)于2021年10月4日正式发布,正式版源代码也于当日被推送到AOSP Android开源项目。截止到笔者撰写这篇文章时,国内各终端厂商的在售Android设备,已经逐步开启了Android 12正式版本的更新。当前,对于Android应用开发者来说,Android 12 的软件兼容适配已迫在眉睫。

bjxiaxueliang的CODING小栈 1万+

Android笔记(二十四):gradle写一个android12自动适配exported脚本,支持aab

由于Google play的政策,提审aab的时候需要适配android12,适配android12最大的工作就是在AndroidManifesst.xml文件中声明的四大组件,都要显式声明exported字段。如果手动添加,在遇到第三方sdk时就会比较麻烦,所以只能使用gradle脚本自动添加,而网上给的gradle脚本例子都是只对apk生效,所以自己手动改造一下,以支持aab。遍历AndroidManifest.xml文件的每一个节点,识别到activity,receiver,service节点时,如果

ZBM来了 1147

Android 12 exported自动适配

最新项目需要升级Android 12,关于Android 12的新特性,大家可以参考Android 12 新特性预览,对于我们开发者来说,必须要适配的两点是:android:exported 和 SplashScreen 。此处我们重点讲一下android:exported 根据官方的介绍,从 Android 12 (即TargetSDK 31)开始,四大组件如果使用了 intent-filter, 但是没显性质配置 exported App 将会无法安装,甚至会出现编译不通过。 android:expo

xiangzhihong8的专栏 1222

我的android12适配遇到的问题

android 12适配的实操

天下皆白,唯我独黑 2623

工业场景落地:Java+PaddleOCR实现高性能OCR文字识别的终极方案

本文深度解析基于Java的高性能工业级OCR实现方案,集成PP-OCRv5深度学习模型,提供: ✅ 多场景支持:手写体、竖排文本、票据表格的精准识别 ✅ 工业级性能:毫秒级响应,支持GPU加速与高并发处理 ✅ 开箱即用:通过Maven快速集成,5行代码完成文字识别 ✅ 完整技术栈:覆盖文本检测、方向校正、识别后处理全流程 附可运行示例代码,助您快速构建企业级OCR系统!

deng775747758的博客 3612

CXL3.0 CDAT(Coherent Device Attributes Table)

在说明CDAT之前,先讲一下CXL中的DOE类型:CXL协议里定义了两种DOE的类型,一种是Compliance就是合规性测试的内容,还有一种就是CDAT(一致性属性表)Memory_Info_Valid是CXL设备的内存配置完成标志,位于DVSEC(Designated Vendor-Specific Extended Capability)的CXL Range X Size Low寄存器中(X为1或2)。

Summer的博客 1437

Android Manifest之<provider>元素中文注释

语法               android:enabled=["true" |"false"]           android:exported=["true" |"false"]           android:grantUriPermissions=["true" | "false"]           android:icon="drawable resource

JewLeo的专栏 1万+

数据预处理的分箱操作

数据分箱 介绍 我们在建立模型前,一般需要对特征变量进行离散化,特征离散化后,模型会更稳定,降低模型过拟合的风险。尤其是采用 logsitic 建立评分卡模型时,必须对连续变量进行离散化。而特征离散化处理通常采用的就是分箱法,数据分箱(也称为离散分箱或分段)是一种数据预处理技术,用于减少次要观察误差的影响,提高泛化性。 数据分箱又分为有监督分箱和无监督分箱,是否使用标签进行离散化(分箱)决定了有监督还是无监督的离散化方法。 知识点 无监督分箱 split 分箱 merge 分箱 无监督分箱 这里为了

随笔记 5334

AndroidManifest内部标签属性---一篇可能就够了

一、基本标签 1. <?xml version="1.0" encoding="utf-8"?> version: xml的版本 encoding: 编码格式 2.AndroidManifest 每一个Android应用都包含一个manifest文件,即AndroidMnifest.xml,用于配置应用的各方面信息; 3. manifest 是And...

树叶的博客 3889

【Dify精讲】第2章:后端服务架构深度剖析

Dify后端采用Flask应用工厂模式实现模块化开发,通过蓝图架构将API分为控制台、Web应用和服务接口三大模块。其设计亮点包括:1) 应用工厂模式支持多环境配置和独立测试;2) 蓝图分层管理实现API模块化,各模块职责清晰;3) 严格遵循RESTful原则设计API接口;4) 使用Flask-RESTful扩展简化API开发。这种架构既保持了Flask的灵活性,又实现了企业级应用的可维护性,是Flask项目架构的优秀实践案例。

wiyi9891的专栏 2127

Basler相机和pylon viewer 的使用

文章目录1 线路连接2 安装和使用pylon 1 线路连接 电源线接到电源插座,网线接到电脑(电脑没有网口的话,可用转换器转到type-c接口)。 2 安装和使用pylon 参考 Basler pylon安装及对网口相机基本设置 相机和电脑以太网IP设置参考:balser相机IP设置 关于相机参数和pylon viewer的详细设置,可以参考pylon Viewer安装路径下的文档(Documentation) ...

Deephome的博客 1万+

Android 12 自动适配 exported 深入解析避坑 | 开发者说·DTalk

本文原作者: 恋猫de小郭,原文发布于: GSYTech众所周知,从 Android 12 开始,使用了 TargetSDK 31 之后,四大组件如果使用了 intent-filter,但是没显性质配置exportedApp 将会无法安装,甚至编译不通过。比如启动的Activity就需要设置ex‍ported为true,至于其他组件是否设置为 true 则...

Android开发者 1877

内网(极空间)搭建gitlab跳板机转发端口及域名配置

然后,回头尝试用内网地址+15022 clone,成功,用公网的ip+15222 clone失败,那就是定位到时nginx转发的问题了。首先想到的就是 git clone是通过ssh的22端口进行下载的,那就需要打通22端口,则又配置22端口的nginx转发。然后,我通过ssh命令,做一个端口转发,以实现内网穿透,将服务端口链接到跳板机上。然后穿透到阿里云服务器跳板机的端口,只能内网访问,需要用nginx做个转发,才能用公网ip进行访问。然后,如上,我的80端口,映射15080,22端口映射15022。

God丶Davide的博客 6076

Android 13 自动适配 exported 深入解析避坑

这块网上各个大佬之前都是采取脚本的方式解决的,原理其实也可简单,就是在打包过程中检索所有没有设置 exported 的组件,给他们动态配置上 exported,这里有个特殊需要注意的是,因为启动 Activity 默认就是需要被 Launcher 打开的,所以 "android.intent.action.MAIN" 需要 exported 设置为 true。修改之后,会影响原有的 manifest 代码风格,需要重新格式化一下,相比默认的,增加了不少空格,暂时不知道怎么解决。

chenhuakang的博客 804

Splunk CVE-2018-11409认证绕过漏洞深度解析

Splunk作为主流企业级日志分析平台,其Web管理接口的认证机制直接关系到整个安全运营体系的可信边界。CVE-2018-11409本质上是REST API路由层会话校验缺失引发的未授权访问问题,根源在于`/en-US/splunkd/__raw/`路径绕过了splunkweb与splunkd之间的信任链验证,导致配置文件、用户哈希、App源码等敏感信息明文泄露。该漏洞凸显了Web接口权限模型设计失守的风险,尤其在默认配置未加固、HTTPS未启用、初始密码未修改的生产环境中危害急剧放大。结合vulfocus

weixin_30875157的博客 599

2024届数字IC秋招-华为机试-数字芯片-笔试真题和答案(五)(含2022年和2023年)

此部分包含华为机试真题,方向是数字芯片,内容详细包括设计、验证、后端等,大部分题目给出参考答案。华为机试都是从题库里抽题目,所以这些题目还是很有参考性的,甚至是完全一样,可以搜到的。(题目内容较多,将分开编写几个文章)

qq_42135020的博客 2083

写了一个适配 Android12-exported 的小插件

从 Android12 开始,如果我们的 tagSdk >=31, 即以 Android 12 或更高版本为目标平台时,且包含使用 intent-fliters,服务,广播,则必须为这些组件声明 android:exported 属性。 这个插件就是帮助大家更好的解决此问题。...

petterp的博客 1110

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Android12 exported适配

当我们设置targetSdk 31(即Android12) 时可能会导致应用无法编译\安装成功。这是因为在Android12以后需要对 包含的四大组件需要显示声明exported属性。1 自己项目的AndroidManifest 那只需要在相关的四大组件上添加 android:exported="true"或android:exported=“false” 就可以了2 aar或sdk中的AndroidManifest 由于aar或sdk是由别人提供的,我们并不方便修改代码,不过......

小天花藏 2503

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Android 12 需要更新适配exported和 SplashScreen

1 android:exported 「它主要是设置 Activity 是否可由其他应用的组件启动」, “true” 则表示可以,而“false”表示不可以。 ❝ 若为“false”,则 Activity 只能由同一应用的组件或使用同一用户 ID 的不同应用启动。 ❞ 当然不止是 Activity, Service 和 Receiver 也会有 exported 的场景。 「一般情况下如果使用了intent-filter,则不能将 exported 设置为“false”」,不然在 Activi..

qq_33209777的博客 1439

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Android 12 快速适配

此模板可确保自定义通知在所有状态下都与其他通知长得一模一样,例如在收起状态下的通知图标和展开功能,以及在展开状态下的通知图标、应用名称和收起功能,与 Notification.DecoratedCustomViewStyle 的行为几乎完全相同。如果你什么都不做,那 App 的 Launcher 图标会变成 SplashScreen 界面的那个图标,而对应的原主题下 windowBackground 属性指定的颜色,就会成为 SplashScreen 界面的背景颜色。而是使用系统标准模板。

金戈鐡馬 1728

Android12 新特性及适配指南

从 Android 12 开始,系统会在应用的冷启动和暖启动时,使用新的启动页 SplashScreen,该启动页默认由应用ICON + 应用主题的windowBackground内容构成。新的启动页中的显示元素可完全由由开发者自定义,官方建议开发者:将未适配Android12前前的应用启动页完全移除,并适配Android12新的启动页,从而避免启动页重复、减少加载时间的问题。Android12开始,对于以Android 12 为目标平台的应用,通知栏的自定义试图将不再使用完整的通知栏区域。

我的专栏 4289

Android 12适配

Android:exported 属性 在 Android 12 中包含 的 activity 、 service 或 receiver 必须为这些应用组件显示声明 android:exported 属性,如下所示。 <activity android:name=".TestActivity" android:exported="false"> <intent-filter> ...... </intent-filter>

zhangshiwen的博客 4373

适配 Android 12 遇到的坑

我们升级到Targetsdk29有大半年时间了,今年为了满足审查去除蓝牙的精确定位权限,以及满足上架Google Play的要求,需要将Targetsdkversion升级到31,适配到Android12。这个过程遇到不少坑,这里记录一下,希望能对大家有所帮助。 由于我们在适配Android 29的时候已经适配了Scoped storage,因此这篇文章里面没有这部分的描述。 1 安全组件输出,exported 编译时报错: android:exported needs to

chuyouyinghe的专栏 1万+
上一篇: ASM 字节码插桩:监控大图加载
下一篇: Android 面试基础坑,你掉进去过几次?
懂你的大海
博客等级 码龄6年 9049粉丝 1381原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值