Android 12 快速适配

news2024/11/18 19:45:50

Android 12 需要更新适配点并不多,本篇主要介绍最常见的两个需要适配的点:android:exported[1]SplashScreen[2]

一、android:exported

它主要是设置 Activity 是否可由其他应用的组件启动, “true” 则表示可以,而“false”表示不可以。

若为“false”,则 Activity 只能由同一应用的组件或使用同一用户 ID 的不同应用启动。

当然不止是 Activity, Service 和 Receiver 也会有 exported 的场景。

一般情况下如果使用了 intent-filter,则不能将 exported 设置为“false”,不然在 Activity 被调用时系统会抛出 ActivityNotFoundException 异常。

相反如果没有 intent-filter,那就不应该把 Activity 的 exported 设置为true , 这可能会在安全扫描时被定义为安全漏洞

而在 Android 12 的平台上,也就是使用 targetSdkVersion 31 时,那么你就需要注意:

如果 Activity 、 Service 或 Receiver 使用 intent-filter ,并且未显式声明 android:exported 的值,App 将会无法安装。

这时候你可能会选择去 AndroidManifest 一个一个手动修改,但是如果你使用的 SDK 或者第三方库没有支持怎么办?或者你想要打出不同 target 平台的包?这时候下面这段 gradle 脚本可以给你省心:

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()
        }
    }

}

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()

            }

        }
    }
}

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

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

如果有需要,还可以自己增加判断设置了 "intent-filter" 的才配置 exported 。

二、SplashScreen

Android 12 新增加了 SplashScreen[3] 的 API,它包括启动时的进入应用的动作、显示应用的图标画面,以及展示应用本身的过渡效果。

它大概由如下 4 个部分组成,这里需要注意:

  • 最好是矢量的可绘制对象,当然它可以是静态或动画形式。

  • 是可选的,也就是图标的背景。

  • 与自适应图标一样,前景的三分之一被遮盖 。

  • 就是窗口背景。

启动画面动画机制由进入动画和退出动画组成。

  • 进入动画由系统视图到启动画面组成,这由系统控制且不可自定义。

  • 退出动画由隐藏启动画面的动画运行组成。如果要对其进行自定义[4],可以通过 SplashScreenView[5] 自定义。

更详细的介绍这里就不展开了,有兴趣的可以自己看官方的资料:developer.android.com/guide/topic…[6] ,这里主要介绍下如何适配和使用的问题。

首先不管你的 TargetSDK 什么版本,当你运行到 Android 12 的手机上时,所有的 App 都会增加 SplashScreen 的功能

如果你什么都不做,那 App 的 Launcher 图标会变成 SplashScreen 界面的那个图标,而对应的原主题下 windowBackground 属性指定的颜色,就会成为 SplashScreen 界面的背景颜色。这个启动效果在所有应用的冷启动和热启动期间会出现。

其实不适配好像也没啥问题。

关于如何迁移和使用 SplashScreen 可以查阅官方详细文档:developer.android.com/guide/topic…[7]

另外还可以参考 《Jetpack新成员SplashScreen:打造全新的App启动画面》[8] 这篇文章,文章详细介绍了如果使用官方的 Jetpack 库来让这个效果适配到更低的 Target 平台。

而正常情况下我们可以做的就是:

  • 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 添加这个主题,不同目录下使用不同主题来达到适配效果。

三、其他

1、通知中心又又又变了

Android 12 更改了可以完全自定义通知外观和行为,以前自定义通知能够使用整个通知区域并提供自己的布局和样式,现在它行为变了

使用 TargetSDK 为 31 的 App,包含自定义内容视图的通知将不再使用完整通知区域;而是使用系统标准模板。

此模板可确保自定义通知在所有状态下都与其他通知长得一模一样,例如在收起状态下的通知图标和展开功能,以及在展开状态下的通知图标、应用名称和收起功能,与 Notification.DecoratedCustomViewStyle 的行为几乎完全相同。

2、Android App Links 验证

Android App Links 是一种特殊类型的 DeepLink ,用于让 Web 直接在 Android 应用中打开相应对应 App 内容而无需用户选择应用。使用它需要执行以下步骤:

如何使用可查阅: developer.android.com/training/ap …[9]

使用 TargetSDK 为 31 的 App,系统对 Android App Links[10] 的验证方式进行了一些调整,这些调整会提升应用链接的可靠性。

如果你的 App 是依靠 Android App Links 验证在应用中打开网页链接,那么在为 Android App Links 验证添加 intent 过滤器时,请确保使用正确的格式, 尤其需要注意的是确保这些 intent-filter 包含 BROWSABLE 类别并支持 https 方案

3、安全和隐私设置

3.1、大致位置

使用 TargetSDK 为 31 的 App,用户可以请求应用只能访问大致位置信息

如果 App 请求 ACCESS_COARSE_LOCATION 但未请求 ACCESS_FINE_LOCATION 那么不会有任何影响。

TargetSDK 为 31 的 App 请求 ACCESS_FINE_LOCATION 运行时权限,还必须请求 ACCESS_COARSE_LOCATION 权限。当 App 同时请求这两个权限时,系统权限对话框将为用户提供以下新选项:

3.2、SameSite Cookie

Cookie 的 SameSite 属性决定了它是可以与任何请求一起发送,还是只能与同站点请求一起发送。

  • 没有 SameSite 属性的 Cookie 被视为 SameSite=Lax。

  • 带有 SameSite=None 的 Cookie 还必须指定 Secure 属性,这意味着它们需要安全的上下文,需要通过 HTTPS 发送。

  • 站点的 HTTP 版本和 HTTPS 版本之间的链接现在被视为跨站点请求,因此除非将 Cookie 正确标记为 SameSite=None; Secure,否则 Cookie 不会被发送。

WebView devtools[11]切换界面标志 webview-enable-modern-cookie-same-site[12],可以在测试设备上手动启用 SameSite 行为。

4、应用休眠

Android 12 在 Android 11(API 级别 30)中引入的自动重置权限行为[13] 的基础上进行了扩展。

如果 TargetSDK 为 31 的 App 用户几个月不打开,则系统会自动重置授予的所有权限并将App 置于休眠状态。

更多可以查阅: developer.android.com/topic/perfo …[14]

四、最后

大致需要注意的就是这些,基本上其实除了 exproted 和 SplashScreen 之外,其他基本都不怎么需要适配,事实上 SplashScreen 我个人觉得会很遭产品嫌弃,毕竟 Material Design 在国内的待遇确实有点惨,没办法去掉 SplashScreen 这点估计需要和产品扯皮一段时间,不过产品和设计一般没有 Android 手机。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/374845.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

满汉楼练习 马踏棋盘

1. 满汉楼 1.结构图 2. 数据库 pwd CHAR(32) NOT NULL DEFAULT ‘’,# 密码&#xff0c;32位 INSERT INTO employee VALUES(NULL, ‘666’, MD5(‘123456’), ‘老韩’, ‘经理’); MD5(‘123456’)是经过MD5加密过后的32位的字符串&#xff0c;用来保存密码 select * fro…

辉光管时钟学习制作及开源软硬件工程

文章目录前言开源地址辉光管项目介绍辉光管的工作条件硬件部分部分介绍充电电路驱动电路不足之处软件部分总结前言 作为一个电子人&#xff0c;一直想做一个辉光管时钟&#xff0c;算是大学的一个心愿&#xff0c;终于在快要毕业前做了一个&#xff0c;下面把软件和硬件的部分…

Windows事件日志监控

大多数数据泄露属内部人员而为&#xff0c;但各企业在监控内部网络活动方面仍存在不足。无论是大型还是小型企业&#xff0c;监控内部网络活动已成为其主要要求。要保护网络安全以防范泄露和威胁&#xff0c;各企业需要采取积极的措施来保证其网络和数据的安全性。监控事件日志…

NCRE计算机等级考试Python真题(四)

第四套试题1、以下选项中&#xff0c;不属于需求分析阶段的任务是&#xff1a;A.需求规格说明书评审B.确定软件系统的性能需求C.确定软件系统的功能需求D.制定软件集成测试计划正确答案&#xff1a; D2、关于数据流图&#xff08;DFD&#xff09;的描述&#xff0c;以下选项中正…

跨境人都在用的指纹浏览器到底有什么魔力?三分钟带你了解透彻

什么是指纹浏览器&#xff1f;这是东哥近期收到最多的粉丝私信咨询&#xff0c;指纹两个字大家都很熟悉&#xff0c;指纹浏览器就变得陌生起来。之前东哥也跟大家分享过很多次指纹浏览器的用法&#xff0c;鉴于还是很多人不认识这个好用的工具&#xff0c;东哥今天就来详细给大…

【ICCV2022】 CAPAO:一种高效的单阶段人体姿态估计模型

CAPAO&#xff1a;一种高效的单阶段人体姿态估计模型 重新思考关键点表示&#xff1a;将关键点和姿态建模作为多人姿态估计的对象&#xff08;Rethinking Keypoint Representations: Modeling Keypoints and Poses as Objects for Multi-Person Human Pose Estimation&#xf…

k8s学习之路 | Day16 k8s 中的容器初探

文章目录容器镜像镜像名称镜像拉取策略私有仓库的拉取策略容器的环境变量和启动命令容器的环境变量容器的启动命令容器的生命周期钩子postStartpreStop容器的探针startupProbelivenessProbereadinessProbek8s 集群中最小的管理单元就是一个Pod&#xff0c;而Pod里面才是容器&am…

linux下devmem访问物理寄存器MT7621 mdio控制

在同专栏的mdio访问phy的三种方式篇&#xff0c;我们着重介绍了通过ioctrl的方式将mdio总线与网卡绑定进行访问&#xff0c;但是实时上数据接口和管理接口可以独立去控制&#xff0c;很不幸&#xff0c;作者现在必须把mdio与网卡解除绑定并独立操控&#xff0c;因此接下来将详细…

【elementUI】基于elementUI自定义封装分页内容

文章目录前端分页的封装后端进行分页的封装&#xff0c;利用el-pagination网页获取数据进行分页主要有前端分页和后端分页&#xff0c;对于数据量较小的数据&#xff0c;可以使用前端分页进行实现。但是一般的分页都是后端获取分页实现前端分页的封装 思路&#xff1a; 1.假设一…

Mybatis源码学习笔记(五)之Mybatis框架缓存机制原理解析

1 Mybatis框架的缓存模块 MyBatis 内置了一个强大的事务性查询缓存机制&#xff0c;它可以非常方便地配置和定制。Mybatis框架中的缓存分为一级缓存和二级缓存&#xff0c;三级缓存基本都要借助自定义缓存或第三方服务来进行实现。但本质上是一样的&#xff0c;都是借助Cache接…

只会手工测试,裸辞后怎么才能找到工作

我们可以从以下几个方面来具体分析下&#xff0c;想通了&#xff0c;理解透了&#xff0c;才能更好的利用资源提升自己。 一、我会什么&#xff1f; 先说第一个我会什么&#xff1f;第一反应&#xff1a;我只会功能测试&#xff0c;在之前的4年的中我只做了功能测试。内心存在…

如何改变照片的大小kb?照片怎么改到100kb?

在平时的日常工作生活当中&#xff0c;我们都会遇到需要上传照片的情况&#xff0c;但是随着拍摄的照片越来越清晰照片体积也越来越大&#xff0c;很容易遇到图片太大上传不成功的情况&#xff0c;那么这时候应该怎么办呢&#xff1f;今天来给大家分享一款照片压缩器&#xff0…

TCP/IP协议,网络工程部分

这个博客参考了许多up主的视频和网上其他的博主的文章&#xff0c;还有我老师的ppt 这里是目录一、osi七层模型&#xff08;参考模型&#xff09;1.物理层2.数据链路层&#xff08;数据一跳一跳进行传递&#xff09;3.网络层&#xff08;端到端传输&#xff09;4.传输层&#x…

synchronized底层如何实现?什么是锁的升级、降级?

第16讲 | synchronized底层如何实现&#xff1f;什么是锁的升级、降级&#xff1f; 我在上一讲对比和分析了 synchronized 和 ReentrantLock&#xff0c;算是专栏进入并发编程阶段的热身&#xff0c;相信你已经对线程安全&#xff0c;以及如何使用基本的同步机制有了基础&#…

Web Spider案例 网洛者 第一题 JS混淆加密 - 反hook操作 练习(五)

文章目录一、资源推荐二、第一题 JS混淆加密 - 反hook操作2.1 过控制台反调试(debugger)2.2 开始逆向分析三、python具体实现代码四、记录一下&#xff0c;execjs调用混淆JS报错的问题总结提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、资源推荐 …

Echarts 实现电池效果的柱状图

第022个点击查看专栏目录本示例是解决显示电池电量状态的柱状图&#xff0c;具体的核心代码请参考源代码。 文章目录示例效果示例源代码&#xff08;共102行&#xff09;相关资料参考专栏介绍示例效果 示例源代码&#xff08;共102行&#xff09; /* * Author: 还是大剑师兰特…

aws ecs 使用application autoscaling自动扩缩ecs服务

参考资料 https://aws.amazon.com/cn/blogs/china/microservices-on-amazon-ecs-1/ https://aws.amazon.com/cn/blogs/china/microservices-on-amazon-ecs-2/ https://zhuanlan.zhihu.com/p/355383555 https://docs.amazonaws.cn/en_us/AmazonECS/latest/developerguide/ser…

YOOV人事管理|2023年面临7大职场趋势,关系到管理者和HR

各种停摆浪潮席卷了2022年的职场生态&#xff0c;对于人力资源工作者来说&#xff0c;无论是判断员工的意向&#xff0c;或是组织面对的挑战&#xff0c;都愈来愈复杂。YOOV人事管理针对2023年&#xff0c;提出了7项观察&#xff0c;提醒雇主和HR应留意的未来工作趋势。 1.安静…

95.【操作系统-第一章】

操作系统(一)、操作系统概述1.1_操作系统的概念、功能和目标(1).操作系统的定义(2).操作系统的功能和目标——作为系统资源的管理者(3).操作系统的功能和目标——向上层提供方便易用的服务(4).操作系统的功能和目标--作为用户和计算机硬件之间的接口(5).操作系统的功能和目标—…

腾讯前端二面常考vue面试题(附答案)

虚拟DOM真的比真实DOM性能好吗 首次渲染大量DOM时&#xff0c;由于多了一层虚拟DOM的计算&#xff0c;会比innerHTML插入慢。正如它能保证性能下限&#xff0c;在真实DOM操作的时候进行针对性的优化时&#xff0c;还是更快的。 MVVM的优缺点? 优点: 分离视图&#xff08;V…