升级 Android 目标版本到 31(S) 居然这么多坑

firmbee-com-gcsNOsPEXfs-unsplash.jpeg

1、背景

最近对我的应用做了一些小的改动,准备上架 Google Play. 本以为小事一桩,没想到要做非常多的改动。其中一个改动是谷歌要求 billing 库升级到 4.0 及以上版本。此外,另一个比较大的改动是要求升级目标版本到 31.

截屏2022-12-12 20.38.40.png

我之前从 29 升级到 30 那次改动已经非常大了。这次应该不会太多改动,没想到总归还是 too young too simple, sometimes naive. 升级目标版本到 31 也不是那么简单。下面是谷歌官方提供的两个文章,分别详细列举了升级到版本的变更以及升级到 Android 12 的详细的变更:

符合 Google Play 的目标 API 级别要求

行为变更:以 Android 12 为目标平台的应用

谷歌官方已经给了详细的说明,这里我分享我在适配过程中遇到的问题和解决思路。

2、exported 属性

本次适配需要做的最明显的一个变更是修改 exported 属性。这个属性是之前就存在。我之前只在个别几个 Service 属性中使用了它。在 31 上开始要求开发者明确指定组件的 exported 属性。

对于没有声明 exported 属性的应用,在启动的过程中就会抛出如下异常,

[图片上传失败...(image-6feea4-1670856933201)]

对于 exported 属性,你可以查看谷歌官方文档的详细解释:android:exported

适配这个属性并不难,只需要在 manifest 中明确指定每个组件的 exported 属性即可。一般来说,遵循如下原则:如果组件中使用了 intent-filter 等属性,那么它大概率是需要对外暴露的,此时需要将 exported 属性直为 true,其他情况下置为 false 即可。

对于引用的三方类库中的 xml 属性也可以通过覆写声明方式增加 exported 以兼容处理,

<activity android:name="com.squareup.leakcanary.internal.DisplayLeakActivity"
    android:exported="false"/>

3、PendingIntent 的变动

这是一个隐藏的变动,非常坑又不像 exported 属性那样容易被察觉。这边变动主要是要求开发者指定在创建 PendingIntent 的时候传入的 flags 参数的可变性。

截屏2022-12-12 20.46.55.png

这可以通过在之前的 flags 基础上增加 FLAG_MUTABLEFLAG_IMMUTABLE 两个属性来完成。比如,之前我的 flags 是,PendingIntent.FLAG_CANCEL_CURRENT,当我想将其修改为不可变的时候,就可以使用如下方式进行修改:

val flags = PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE

它可能是需要改动最多的一个变动,根据我在项目中修改的情况来看,以下几个场景需要排查:

  • 桌面小控件 Appwidget
  • 通知 Notification
  • 桌面快捷方式 Shortcut

同时从几个方向来检索项目中需要改动的地方:

  • 直接检索 PendingIntent 的 flags 的调用,比如 FLAG_ONE_SHOT, FLAG_NO_CREATE, FLAG_CANCEL_CURRENTFLAG_UPDATE_CURRENT 等,建议查看源码之后进行检索
  • PendingIntent 的静态方法工厂,比如 PendingIntent.getBroadcast()PendingIntent.getActivity() 等。因为,获取 PendingIntent 的时候需要指定 flags 参数。

那么,另一个问题来了,究竟什么时候该选择 FLAG_MUTABLE,什么时候该选择 FLAG_IMMUTABLE 呢?

它的注释是这么说的,

Flag indicating that the created PendingIntent should be immutable. This means that the additional intent argument passed to the send methods to fill in unpopulated properties of this intent will be ignored.
FLAG_IMMUTABLE only limits the ability to alter the semantics of the intent that is sent by send by the invoker of send. The creator of the PendingIntent can always update the PendingIntent itself via FLAG_UPDATE_CURRENT.

也就是说,FLAG_IMMUTABLE 的“不可变”指的是,当 PendingIntent 设置了 flags 为“不可变”之后,调用它的 send 方法时传入的 Intent 将会被忽略。

这里举一个具体的场景,比如在列表类的 Appwidget 里,我们会使用 PendingIntent 设置列表的某一项的点击事件。考虑到列表量比较大,为每一个列表条目都声明一个 PendingIntent 显然开销太大。所以,Android 的处理机制是,

val views = RemoteViews(context.packageName, R.layout.layout_appwidget_note_list)
val i = Intent(context, MainActivity::class.java)
i.action = ACTION_APPWIDGET_NOTE_CLICK
val pi = PendingIntent.getActivity(context, 0, i, FLAG_CANCEL_CURRENT_MUTABLE)
views.setPendingIntentTemplate(R.id.lv, pi)

如上所示,首先定一个一个 PendingIntent,并调用 RemoteViews 的 setPendingIntentTemplate 方法传入,作为一个模版。然后在 RemoteViewsFactory 的 getViewAt 方法中为每个列表项设置点击时的 Intent,

val row = RemoteViews(context.packageName, R.layout.item_appwidget_note)
val i = Intent().putExtras(extras)
row.setOnClickFillInIntent(R.id.root, i)

当用户触发了点击事件的时候,系统会在调用 PendingIntent 的 send 方法时将 Intent 传入并唤起组件。此时,如果我们将 PendingIntent 的 flags 设置为 FLAG_IMMUTABLE,那么这里发送时传入的 Intent 参数将被忽略,因此可能导致虽然唤起了其他组件,但是参数丢失的情况。而对于那种,声明 PendingIntent 时就传入了 Intent 的时候,一般来说不需要设置为 FLAG_MUTABLE 的。

以上是 PendingIntent 的改动,刚好在我的项目里两种情况都有遇到,所以详细分析了一下。

4、构建项目 JDK 需要升级

当将项目的 targetSdkVersion 升级到了 31 之后,构建项目的时候可能会遇到如下异常,

截屏2022-12-12 20.18.46.png

当然你也可能不会遇到这个问题。那主要的原因是,你的 Android Studio 里 Gradle 构建时用到的版本已经是 Java 11 的了。可以通过 Preference->Build->Gradle 查看当前 Android Studio 中使用的 JDK 版本,

截屏2022-12-12 20.19.45.png

Gradle JDK 处修改构建时用的 JDK 版本即可。

以上是针对 Android Studio 构建时的情况。但当我们使用脚本或者命令行构建项目的时候需要用到的就不是 Android Studio 的 JDK 版本了。此时,可以通过 java --version 查看环境变量中配置的 JDK 版本。

截屏2022-12-12 20.20.31.png

我们不能直接修改环境变量中的 JDK 版本解决上述编译问题。因为毕竟除了开发,我们可能还有很多其他应用在使用 JDK 环境。此时,我们可以通过 Gradle 构建时的命令来指定构建时使用的 JDK。

gradlew -Dorg.gradle.java.home=你的 JDK 路径

对于打包脚本的修改我也更新到了项目 autopackage 中。

总结

以上是个人升级项目的目标版本到 31 过程中遇到的一些典型的问题。此外,在适配的过程中可能还有许多其他细节需要修改,对此,参考文首的几个链接了解即可。升级 31 虽然没有升级到 30 改动多,但是对大型的项目来说,需要改动的点应该还是挺多的。

如有疑问,欢迎交流!👏

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容