-
2022-03-04 17:32:11
1.给Manifest的四大组件配置属性"exported",如果有</intent-filter/>标签,需要配置成true,其他默认false。如果没有将会抛出异常。
Targeting S+ (version 10000 and above) requires that an explicit value for android:exported be defined when intent filters are present
<activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
2.在 Android 12 中创建 PendingIntent 的时候,使用 PendingIntent.FLAG_MUTABLE 或 PendingIntent.FLAG_IMMUTABLE 标志,Android Studio 强烈建议PendingIntent.FLAG_IMMUTABLE。因为抛出的异常里面就是这么说的。
java.lang.IllegalArgumentException: com.zhenpin.luxurystore: Targeting S+ (version 31 and above) requires that one of be specified when creating a PendingIntent.
Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality depends on the PendingIntent being mutable, e.g. if it needs to be used with inline replies or bubbles.PendingIntent contentIntent = PendingIntent.getActivity( App.getInstance(), 0, intent, PendingIntent.FLAG_IMMUTABLE);
3.Bluebooth蓝牙权限变更,在31及以上请添加一下内容,动态判断申请权限
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
更多相关内容 -
android12适配
2022-03-08 13:00:10前言:当我们的targetSdkVersion 31指定了最高支持android12时,若没有做文中所介绍的适配,则应用在android12设备上会安装失败(虽然debug时非android12手机可以编译)。 比如我们的上架小米应用市场时,自动...前言:当我们的targetSdkVersion 31指定了最高支持android12时,若没有做文中所介绍的适配,则应用在android12设备上会安装失败(虽然debug时非android12手机可以编译)。
比如我们的上架小米应用市场时,自动依据targetSdkVersion 检测我们apk,若当我们指定31时,没做适配的情况下,通常小米应用市场会上架审核不通过,如:
1、使用adb install命令连续三次无法安装成功。请自行使用adb命令对andorid版本:12进行测试。
并不是因为install和install -r的问题,是因为没做最新的android12适配。如果简单跳过小米市场的自动检测,也可以适当调低targetSdkVersion至31以下。本文主要介绍如何在已经要支持android12的情况下,代码如何调整。
一、android:exported
这个字段出现在我们的AndroidManifest.xml中,作用是:是否支持其它应用调用当前组件。
首先adnroid12官方申明:
Android四大组件 Activity,Service,Provider,Receiver 四大组件中都具有该属性:如果四大组件中有intent-filter节点,则需要指定android:exported为true或为false。
那么我们自己的AndroidManifest.xml文件我们肯定知道在哪里调整,但是第三方库我们就不知道了,此时我们可以在我们的app.build.gradle文件中统一修改:
如:当我们的com.android.tools.build:gradle版本在4.1.0或以上,我们可以在app.build.gradle文件中新增如下代码:
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") if (manifestOutFile.exists() && manifestOutFile.canRead() && manifestOutFile.canWrite()) { def manifestFile = manifestOutFile def xml = new XmlParser(false, false).parse(manifestFile) def exportedTag = "android:exported" def nameTag = "android:name" def nodes = xml.application[0].'*'.findAll { (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 } } } } } it.attributes().put(exportedTag, "${isMain}") } PrintWriter pw = new PrintWriter(manifestFile) pw.write(groovy.xml.XmlUtil.serialize(xml)) pw.close() } } } }
二、此时我们可以通过反编译软件观察我们的打包后的AndroidManifest.xml样子:
处理后(本文仅仅以mainActivity为例,第三方库原理一样):
我们可以看下处理前:
如还有疑问,欢迎随时私聊版主
-
Android 12适配
2022-02-18 09:29:29在 Android 12 中包含 的 activity 、 service 或 receiver 必须为这些应用组件显示声明 android:exported 属性,如下所示。 <activity android:name=".TestActivity" android:exported="false"> <...Android:exported 属性
在 Android 12 中包含 的 activity 、 service 或 receiver 必须为这些应用组件显示声明 android:exported 属性,如下所示。
<activity android:name=".TestActivity" android:exported="false"> <intent-filter> ...... </intent-filter> </activity>
如果在包含 的 activity 、 service 或 receiver 组件中,没有显示声明 android:exported 的值,应用将无法安装,错误日志如下所示。
Installation did not succeed. The application could not be installed: INSTALL_FAILED_VERIFICATION_FAILURE List of apks: [0] '.../build/outputs/apk/debug/app-debug.apk' Installation failed due to: 'null'
如果应用在需要声明 android:exported 的值时未进行此声明,错误日志如下所示。
Targeting S+ (version 10000 and above) requires that an explicit value for \ android:exported be defined when intent filters are present
目前已经有很多开源项目都已经开始适配这个行为的变更了,例如 leakcanary 等等
这个行为的变更无论是对库开发者 和 还是应用开发者影响都非常大。
为什么在 Android 12 上需要显示声明 android:exported 属性
android:exported 属性的默认值取决于是否包含 ,如果包含 那么默认值为 true,否则 false。
• 当 android:exported=“true” 时,如果不做任何处理,可以接受来自其他 App 的访问。
• 当 android:exported=“false” 时,限制为只接受来自同一个 App 或一个具有相同 user ID 的 App 的访问。
在 Android 12 的平台上,也就是使用 targetSdkVersion 31 时,那么需要注意:
「如果 Activity 、 Service 或 Receiver 使用 intent-filter ,并且未显式声明 android:exported 的值,App 将会无法安装。」
这时候你可能会选择去 AndroidManifest 一个一个手动修改,但是如果使用的 SDK 或者第三方库没有支持怎么办?或者想要打出不同 target 平台的包?这时候可以使用下面这段 gradle 脚本:
注意:com.android.tools.build:gradle:4.1.0 以上版本/** * 修改 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() } } } }
com.android.tools.build:gradle:3.4.3 以下版本
/** * 修改 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() } } }
这段脚本你可以直接放到 app/build.gradle 下执行,也可以单独放到一个 gradle 文件之后 apply 引入,它的作用就是:
「在打包过程中检索所有没有设置 exported 的组件,给他们动态配置上 exported」。这里有个特殊需要注意的是,因为启动 Activity 默认就是需要被 Launcher 打开的,所以 “android.intent.action.MAIN” 需要 exported 设置为 true 。
❝
如果有需要,还可以自己增加判断设置了 “intent-filter” 的才配置 exported。
❞
指定 PendingIntent 的可变性
在 Android 12 中创建 PendingIntent 的时候,需要显示的声明是否可变,请分别使用 PendingIntent.FLAG_MUTABLE 或 PendingIntent.FLAG_IMMUTABLE 标志,如果应用试图在不设置任何可变标志的情况下创建 PendingIntent 对象,系统会抛出 IllegalArgumentException 异常,错误日志如下所示。
PACKAGE_NAME: Targeting S+ (version 10000 and above) requires that one of \ FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent. Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if \ some functionality depends on the PendingIntent being mutable, e.g. if \ it needs to be used with inline replies or bubbles.
PendingIntent认识
PendIntent其实是Intent的封装,这就带来了几个问题:
为什么要有PendingIntent?与Intent有什么区别?
PendingIntent的应用场景主要有哪些?
它的内部是如何实现的?
1.1 与Intent的区别
Intent 是意图的意思。Android 中的 Intent 正是取自这个意思,它是一个消息对象,通过它,Android 系统的四大组件能够方便的通信,并且保证解耦。Intent 可以说明某种意图,携带一种行为和相应的数据,发送到目标组件。
PendingIntent是对Intent的封装,但它不是立刻执行某个行为,而是满足某些条件或触发某些事件后才执行指定的行为
A组件 创建了一个 PendingIntent 的对象然后传给 B组件,B 在执行这个 PendingIntent 的 send 时候,它里面的 Intent 会被发送出去,而接受到这个 Intent 的 C 组件会认为是 A 发的。
B以A的权限和身份发送了这个Intent我们的 Activity 如果设置了 exported = false,其他应用如果使用 Intent 就访问不到这个 Activity,但是使用 PendingIntent 是可以的。
即:PendingIntent将某个动作的触发时机交给其他应用;让那个应用代表自己去执行那个动作(权限都给他)
2.2 获取PendingIntent
关于PendingIntent的实例获取一般有以下五种方法,分别对应Activity、Broadcast、ServicegetActivity()
getActivities()
getBroadcast()
getService()
getForegroundService()
它们的参数都相同,都是四个:Context, requestCode, Intent, flags,分别对应上下文对象、请求码、请求意图用以指明启动类及数据传递、关键标志位。
前面三个参数共同标志一个行为的唯一性,而第四个参数flags:FLAG_CANCEL_CURRENT:如果当前系统中已经存在一个相同的PendingIntent对象,那么就将先将已有的PendingIntent取消,然后重新生成一个PendingIntent对象。
FLAG_NO_CREATE:如果当前系统中不存在相同的PendingIntent对象,系统将不会创建该PendingIntent对象而是直接返回null,如果之前设置过,这次就能获取到。
FLAG_ONE_SHOT:该PendingIntent只作用一次。在该PendingIntent对象通过send()方法触发过后,PendingIntent将自动调用cancel()进行销毁,那么如果你再调用send()方法的话,系统将会返回一个SendIntentException。
FLAG_UPDATE_CURRENT:如果系统中有一个和你描述的PendingIntent对等的PendingInent,那么系统将使用该PendingIntent对象,但是会使用新的Intent来更新之前PendingIntent中的Intent对象数据,例如更新Intent中的Extras
备注:两个PendingIntent对等是指它们的operation一样, 且其它们的Intent的action, data, categories, components和flags都一样。但是它们的Intent的Extra可以不一样2.3 使用场景
关于PendingIntent的使用场景主要用于闹钟、通知、桌面部件。大体的原理是: A应用希望让B应用帮忙触发一个行为,这是跨应用的通信,需要 Android 系统作为中间人,这里的中间人就是 ActivityManager。 A应用创建建 PendingIntent,在创建 PendingIntent 的过程中,向 ActivityManager 注册了这个 PendingIntent,所以,即使A应用死了,当它再次苏醒时,只要提供相同的参数,还是可以获取到之前那个 PendingIntent 的。当 A 将 PendingIntent 调用系统 API 比如 AlarmManager.set(),实际是将权限给了B应用,这时候, B应用可以根据参数信息,来从 ActivityManager 获取到 A 设置的 PendingIntent
PendingIntent可以看作是对*Intent的包装。*PendingIntent主要持有的信息是它所包装的*Intent和当前*Application的*Context。正由于*PendingIntent中保存有当前*Application的*Context,使它赋予带他程序一种执行的*Intent的能力,就算在执行时当前*Application已经不存在了,也能通过存在*PendingIntent里的*Context照样执行*Intent。
为什么在 Android 12 上需要显示的指定 PendingIntent 的可变性
在 Adnroid 12 之前,默认创建一个 PendingIntent 它是可变的,因此其他恶意应用程序可能会拦截,重定向或修改此 Intent。(但是是有条件限制的)
一个 PendingIntent 是一个可以给另一个应用程序使用的 Intent,PendingIntent 接收待处理意图的应用程序可以使用与产生待处理意图的应用程序相同的权限和身份执行待处理意图中指定的操作。
因此,创建待处理意图时必须小心,为了安全性 Google 在 Android 12 中需要开发者自己来指定 PendingIntent 的可变性。
adb 备份限制
Android API Level 8及其以上Android系统提供了为应用程序数据的备份和恢复功能,此功能的开关决定于该应用程序中AndroidManifest.xml文件中的allowBackup属性值,其属性值默认是True。当allowBackup标志为true时,用户即可通过adb backup和adb restore来进行对应用数据的备份和恢复,这可能会带来一定的安全风险。
打开cmd,输入adb backup -f back.ab -noapk 项目包名
输入密码,备份你的数据
这样你备份好了你的数据,然后将你备份的数据输入另外一台设备中。在另一台设备中安装程序,在控制台输入adb restore back.ab,弹出下列界面,输入密码
就可以利用allowBackup 漏洞进行沙盒数据的备份了。
Android 开发者都应该知道这个命令 adb backup , 它可以备份应用的数据,在 Android 12 中,为了保护私有应用数据,用户运行 adb backup 命令时,从设备导出的任何其他系统数据都不包含应用数据。
如果你在测试和开发过程中需要使用 adb backup 来备份应用数据,你可以在 AndroidManifest 中将android:debuggable 设置为 true 来导出应用数据。
<application android:name=".App" android:debuggable="true" ....../>
注意:在发布应用前将 android:debuggable 设置为 false。
为什么在 Android 12 上限制了 adb backup 命令的默认行为
因为这个存在严重的安全问题,当初 Google 为了提供 App 数据备份和恢复功能,可以在 AndroidManifest 中添加 android:allowBackup 属性,默认值为 true, 当你创建一个应用的时候,会默认添加这个属性,如下所示。
<application android:name=".App" android:allowBackup="true" ....../>
当 android:allowBackup=“true” 时,用户可以通过 adb backup 和 adb restore 命令对应用数据进行备份和恢复,也就是说可以在其他的 Android 手机上安装同一个应用,通过如上命令恢复用户的数据。
为了安全起见,我们在发布出去的 Apk 中一定要将 android:allowBackup 属性设置为 false 来关闭应用程序的备份和恢复功能,以免造成信息泄露。国民级应用 XX 信, 在曾今发出的版本中 allowBackup 的属性值是 true,被其他逆向开发者利用之后,现在的版本中这个值已经修改为 false了,有兴趣的小伙们可以反编译看看。
除了 PendingIntent ,另外两个除非特定情况,基本都是 false 处理
SplashScreen
当用户启动应用而应用的进程未运行(冷启动)或 Activity 尚未创建(温启动)时,会发生以下事件。(在热启动期间从不显示启动画面。)
- 系统使用主题以及您已定义的任何动画显示启动画面。
- 当应用准备就绪时,会关闭启动画面并显示应用。
Android 12 新增加了 SplashScreen 的 API,它包括启动时的进入应用的动作、显示应用的图标画面,以及展示应用本身的过渡效果。
它大概由如下 4 个部分组成,这里需要注意:
1 最好是矢量的可绘制对象,当然它可以是静态或动画形式。
2 是可选的,也就是图标的背景。
3 与自适应图标一样,前景的三分之一被遮盖。
4 就是窗口背景。
• 启动画面动画机制由进入动画和退出动画组成。
• 进入动画由系统视图到启动画面组成,这由系统控制且不可自定义。
退出动画由隐藏启动画面的动画运行组成。如果要对其进行自定义,可以通过 SplashScreenView 自定义。
「首先不管你的 TargetSDK 什么版本,当你运行到 Android 12 的手机上时,所有的 App 都会增加SplashScreen 的功能」。
如果你什么都不做
从 Android 12 开始,在所有应用的冷启动和温启动期间,系统一律会应用 Android 系统的默认启动画面。默认情况下,此系统默认启动画面由应用的启动器图标元素和主题的
windowBackground
(如果是单色)构成。其实不适配好像也没啥问题…
而正常情况下我们可以做的就是:
1、升级 compileSdkVersion 31 、 targetSdkVersion 31 & buildToolsVersion ‘31.0.0’
2、 添加依赖 implementation “androidx.core:core-splashscreen:1.0.0-alpha02”
3、增加 values-v31 的目录
4、添加 styles.xml 对应的主题,例如:
<resources> <style name="LaunchTheme" parent="Theme.SplashScreen"> <item name="windowSplashScreenBackground">@color/splashScreenBackground</item> <!--<item name="windowSplashScreenAnimatedIcon">@drawable/splash</item>--> <item name="windowSplashScreenAnimationDuration">500</item> <item name="postSplashScreenTheme">@style/AppTheme</item> </style> </resources>
5、给你的启动 Activity 添加这个主题,不同目录下使用不同主题来达到适配效果。
Android12之前
常规做法
我们知道从点击 Launcher 上的 icon 到 App 内容描画之前,有很多准备工作。在这段时间是看不到目标 App 内容的。
为了快速响应用户的点击或缓解用户的等待,在 App 描画之前系统将启动专用的 SplashScreenWindow 盖在 App 之上。该Window 的呈现源自于 App 主题方面的配置。
配置的不同进而影响到启动 Window 的表现,我们来看看各种配置的做法。
默认的启动画面背景
假使 App 的主题针对 Window 背景什么都不设置,你会发现启动的过程中总有个默认的白画面一闪而过。
<style name="SplashThemeBase.DefaultBg"/>
不设置启动画面背景
当然可以将 windowBackground 设置为空,可是你会发现启动的过程中又变成黑色一片闪过。
<style name="SplashThemeBase.NoBg"> <item name="android:windowBackground">@null</item> </style>
无论是白画面还是黑画面一闪而过,都无法接受,所以还得继续优化。
直接关闭启动画面
无论白画面还是黑画面一闪而过的体验都不好,这时候可能会想到关闭默认的启动画面。通过 windowDisablePreview 属性可以彻底关闭启动画面。
<style name="SplashThemeBase.TransparentBg"> <item name="android:windowDisablePreview">true</item> </style>
这样一来,确实看不到启动画面的存在了,但整个过程貌似“变慢”了。实际上性能并没有**“劣化”**,只是启动中过渡的 Window 不存在了。假使 Application 或 Activity 等组件里存在耗时逻辑的话,这种劣化会更加明显。
相较于前面的黑白画面一闪而过,这种变慢的体验也好不到哪去。
设置启动画面背景
绕来绕去,最后我们发现还是得提供一个恰当的启动画面。具体在于将 UI 提供的背景色、Icon、Brand 等资源组合成一个 LayerListDrawable,然后将其设置到 windowBackground 中即可。
<!--theme--> <style name="SplashThemeBase.WithBg"> <item name="android:windowBackground">@drawable/ic_splash_bg</item> </style> <!--drawable--> <layer-list ... > <item android:drawable="@drawable/ic_logo"></item> <item android:drawable="@drawable/ic_brand"></item> </layer-list>
这种做法既可以提供体验良好的过渡画面,同时可以快速响应用户的点击。可以说是最为简单也最为便捷的一种实现方式。
设置启动画面内容
Android 8 加入一个配置启动画面的属性 windowSplashscreenContent,用于配置启动画面的内容。它的优先级高于 windowBackground。两者一起设置的话呢,会覆盖在 windowBackground 上。大体的效果呢和前面的差不多,不再赘述。
<style name="SplashThemeBase.WithBg.SplashScreenContent"> <item name="android:windowSplashscreenContent">@drawable/ic_splash_content</item> </style>
注意:这个属性自 Android 8 加入,从 12 开始废弃
主题和外观配置
<!-- values-v31/themes.xml --> <!--单一颜色填充「启动画面」窗口背景--> <item name="android:windowSplashScreenBackground">@color/...</item> <!--「启动画面」中心的图标, 可以配置AnimationDrawable 和 AnimatedVectorDrawable类型的drawable--> <item name="android:windowSplashScreenAnimatedIcon">@drawable/...</item> <!--「启动画面」中心图标动画的持续时间,这个属性不会对屏幕显示的实际时间产生任何影响--> <item name="android:windowSplashScreenAnimationDuration">1000</item> <!--「启动画面」中心图标后面设置背景--> <item name="android:windowSplashScreenIconBackgroundColor">@color/...</item> <!--「启动画面」底部显示的品牌图标--> <item name="android:windowSplashScreenBrandingImage">@drawable/...</item>
延长启动画面
val content: View = findViewById(android.R.id.content) content.viewTreeObserver.addOnPreDrawListener( object : ViewTreeObserver.OnPreDrawListener { override fun onPreDraw(): Boolean { // 模拟一些数据的初始化,再取消挂起 return if (viewModel.isReady) { // 取消挂起,恢复页面内容绘制 content.viewTreeObserver.removeOnPreDrawListener(this) true } else { // 挂起,内容还没有准备好 false } } } )
关闭启画面的动画
// 自己定制关闭的动画 splashScreen.setOnExitAnimationListener { splashScreenView -> val slideUp = ObjectAnimator.ofFloat( // 你们自己控制,自己随便写什么动画,这里我们测试让图标移动 splashScreenView.iconView, View.TRANSLATION_Y, 0f, -splashScreenView.height.toFloat() ) slideUp.interpolator = AnticipateInterpolator() slideUp.duration = 200L slideUp.doOnEnd { splashScreenView.remove() } slideUp.start() }
-
Android 12适配安全组件导出设置`android:exported` 指定显式值”
2022-01-22 17:47:36教你如何解决编译错误:“面向 Android 12 及更高版本的应用需要为 `android:exported` 指定显式值”问题 Error: Apps targeting Android 12 and higher are required to specify an explicit value for `android:...如何解决编译错误:“面向 Android 12 及更高版本的应用需要为
android:exported
指定显式值”问题如果您的应用针对 Android12,则文档说:
如果您的应用面向 Android 12 并包含使用Intent 过滤器的活动、服务或广播接收器, 则您必须显式声明这些应用组件的属性android:exported
因此,我为所有在 AndroidManifest.xml 中使用意图过滤器的组件显式声明android:exported了属性,但仍然因编译错误而失败:
清单合并失败并出现多个错误,请参阅日志错误:当相应组件定义了意图过滤器时, 需要针对 Android 12 及更高版本的应用程序为 `android:exported` 指定显式值
发生了什么 ??
为什么所有的activity、server和Receiver都配置exported了,还报这个错误?
您需要检查“合并”清单
该文件说:
您的 APK 文件只能包含一个
AndroidManifest.xml
文件,但您的 Android Studio 项目可能包含多个文件——由主要源集、构建变体和导入的库提供。因此,在构建您的应用程序时,Gradle 构建会将所有清单文件合并为一个打包到您的 APK 中的清单文件。和
提示:使用合并清单视图预览合并清单的结果并查找冲突错误。
因此,您还需要检查导入的库,并且要预览合并的清单,您可以使用编辑器底部的Merged Manifest 选项卡。
此时您需要将 targetSdkVersion暂时降低到 30 才能进行编译。
在 我的项目中,我发现下面显示的这些来自外部库的组件没有为 `android:exported` 指定显式值,即使这些组件声明了意图过滤器。
来自外部库的这些组件声明了意图过滤器,但未指定导出的属性
当我在 Merged Manifest 视图中单击这些组件时,我发现这些组件来自androidx.test:core:1.3.0。
但是,我的项目确实明确地实现了这个库,所以我必须找到其他依赖于“androidx.test:core:1.3.0”的库。
替换库的提示
所以我不得不查找和替换依赖于 androidx.test:core:1.3.0 的库。
总结
如果您指定了exported 属性也编译失败,原因可能是您的外部库。要找出这一点,您可以使用合并清单视图进行检查。要找到合适版本的库替换或者升级。
-
Android 12 适配指南——SplashScreen
2022-03-16 11:30:46Android 12(API 31)引入了 ...若开发者未进行SplashScreen的适配工作,在应用冷启动和温启动时,可能会呈现两个启动页先后出现的情况(Android SplashScreen启动页 + Android应用自定义开发的启动页或引导页)。 -
Android12 新特性及适配指南
2022-03-19 22:04:31Android 12(API 31)于2021年10月4日正式发布,正式版源代码也于当日被推送到AOSP Android开源项目。截止到笔者撰写这篇文章时,国内各...当前,对于Android应用开发者来说,Android 12 的软件兼容适配已迫在眉睫。 -
Android 12 快速适配要点
2021-12-02 22:12:21Android 12 需要更新适配点并不多,本篇主要介绍最常见的两个需要适配的点:android:exported 和 SplashScreen 。 一、android:exported 它主要是设置 Activity 是否可由其他应用的组件启动, “true” 则表示可以,... -
Android 12应用适配指南
2021-05-20 15:05:27Android 12应用适配指南1.Android 12上的主要变更1.1 兼容性1.1.1 前台服务启动限制1.1.2 前台服务通知延迟1.1.3 待处理 intent 必须声明可变性1.1.4 非SDK接口名单更新1.2 用户体验1.2.1 接收内容的统一API1.2.2 ... -
Android 12兼容性适配指南
2021-09-25 16:18:12Android 12 来了,从今年年初开始到如今正式发布前夜,Google已经释放了Beta 5版本,可以在官网下载预览版本进行测试。 测试应用的兼容性十分重要。在每个系统版本中,我们都会对平台进行整体的改进,强化隐私和安全... -
Android屏幕适配smallestWidth适配尺寸文件
2018-09-11 21:17:31Android屏幕适配smallestWidth适配尺寸文件,大部分尺寸,下载放在src/main/res下 -
Android12适配 启动闪退的原因之一
2021-10-14 16:20:22Android12适配 启动闪退的原因之一 Android真的是问题多呀!在公司发布软件几天后软件商城发现并告知我们使用Android12版本安装我们的软件启动直接闪退,我反复测试发现其他Android版本都没问题就Android12版本有... -
是时候了解android12系统适配了
2020-09-29 14:27:16是时候了解android12系统的适配了 -
Android-今日头条屏幕适配方案终极版一个极低成本的Android屏幕适配方案
2019-08-13 06:11:04A low-cost Android screen adaptation solution (今日头条屏幕适配方案终极版,一个极低成本的 Android 屏幕适配方案) -
Android代码-今日头条屏幕适配方案终极版,一个极低成本的 Android 屏幕适配方案
2019-08-06 08:48:51A low-cost Android screen adaptation solution (今日头条屏幕适配方案终极版,一个极低成本的 Android 屏幕适配方案). Overview Pixel 2 XL | 1440 x 2880 | 560dpi: Pixel XL | 1440 x 2560 | 560dpi: ... -
android 万能适配
2016-04-13 13:32:16android 万能屏幕适配,支持手机,pad,针对pad处理是(屏幕大小大于6英寸,自动缩放到6英寸大小。),针对手机,自动全屏缩放。 -
Android 12 行为变更:适配以Android 12为目标的应用
2021-05-24 20:47:45Android 12目前还是开发者预览版,预计到8月份会出正式版,但是官网已经给出了关于以Android 12为目标的应用适配应该注意的事项。 包含 intent 过滤器的应用组件必须声明 exported 属性 以 Android 12 为目标平台的... -
Android:Android12启动页(Splash)适配
2021-11-23 09:40:40官方文档:启动画面 | Android 开发者 | Android Developers 用法,在自定义Splash画面的最初的周期函数(如onCreate)里调用下述方法: private fun extendFlash12Display() { val content: View = ... -
Android dimens适配
2015-12-28 11:22:05Android屏幕宽度不同的手机做的适配,可以适配95%以上的手机。 -
Android生成适配各种分辨率的dimen源文件
2018-04-25 18:07:01自己写了一个Java文件用于生成适配各种Android屏幕分辨率的dimen尺寸,以px为单位,在res文件夹下创建“values-分辨率,如:values-1280x720”的文件再将生成的文件放进去,原理很简单,易懂 -
Android屏幕适配工具类 Android自动生成不同分辨率的值
2021-01-05 09:19:42本文实例为大家分享了Android屏幕适配工具类的具体代码,供大家参考,具体内容如下 DimenTool github地址 Android 屏幕适配方案,自动生成不同分辨率的值 android中官方建议的屏幕适配方式,通过根据不同的分辨率在... -
新版本系统适配: Android 12 中的兼容性变更
2022-01-11 12:28:31随着 Android 12 正式版 的发布,越来越多的用户将升级至最新版本。Android 12 带来大量新 API 和功能更新的同时也带来了平台兼容性的变更,我们建议开发者优先对当前应用进行测试,并进行兼容性更新。这样一来,当... -
Android-Android屏幕适配方案自动生成不同分辨率的值
2019-08-13 07:03:43Android 屏幕适配方案,自动生成不同分辨率的值 -
Android屏幕适配:dimens自动生成工具以及使用方法,亲测好用
2020-08-11 16:12:12Android屏幕适配:dimens自动生成工具以及使用方法,亲测好用,需要通过多套dimens做android屏幕适配的同学可以下载了,里面包括使用方法,方便易懂 -
DialogFragment和AndroidAutoSize 的适配问题
2020-01-09 13:46:08相关博客:【android学习开源项目之AndroidAutoSize】AndroidAutoSize和DialogFragment的适配 https://blog.csdn.net/ljb568838953/article/details/103906449; 本demo的知识点:1.AndroidAutoSize 的使用(今日... -
详解Android 折叠屏适配攻略
2020-08-25 20:14:20主要介绍了Android 折叠屏适配攻略,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 -
个推技术分享:Android12更新详解及适配指南
2021-11-22 15:32:42开发者必看>> -
Android全面屏与异形(刘海)屏的适配教程
2021-01-05 03:27:03Android全面屏的手机越来越多了,要开始考虑应用适配全面屏的问题了,查了查相关文章,总结一下. 声明最大屏幕宽高比 以上图片来自Google Developer 通过文档可以看出从Android7.0开始,应用的多窗口模式默认变为启动,... -
Android的通知栏多版本适配
2019-10-24 19:36:18Andoid解决通知栏图标显示小黑块以及 8.0以上版本的适配和多个版本的通知栏开启状态判断。