前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Android 12 适配升级小结

Android 12 适配升级小结

作者头像
阿策小和尚
发布于 2022-08-30 07:21:01
发布于 2022-08-30 07:21:01
2.9K00
代码可运行
举报
文章被收录于专栏:阿策小和尚阿策小和尚
运行总次数:0
代码可运行

Android 12 发布已经有好几个月了,而且随着各大市场对应用适配的要求逐渐提高,和尚也尝试将一个历史的应用简单升级适配 Android 12

Android 12

Android 12 对应 Build.VERSION_CODES.S,使用 Material You 打造的全新系统界面,富有表现力、活力和个性。使用重新设计的微件、AppSearch、游戏模式和新的编解码器扩展您的应用。支持隐私信息中心和大致位置等新的保护功能。使用富媒体内容插入功能、更简便的模糊处理功能、经过改进的原生调试功能等提高工作效率。

Android 12 相对我们的历史项目来说属于较大版本的更新,在适配过程中遇到一系列问题,和尚简单记录整理一下。

SDK 版本号升级

和尚首先对 SDK 版本号进行升级,之后对升级后的应用逐步进行适配更新;

当前版本
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
minSdkVersion = 17
targetSdkVersion = 28
compileSdkVersion = 28
buildToolsVersion = '28.0.3'
升级后版本
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
minSdkVersion = 17
targetSdkVersion = 31
compileSdkVersion = 31
buildToolsVersion = '31.0.0'

Q1: Gradle 不匹配

升级 SDKsync 后遇到第一个 Gradle 不匹配问题;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Installed Build Tools revision 31.0.0 is corrupted. Remove and install again using the SDK Manager.

A1: 升级 Android 12 对应 Gradle 版本

历史版本 Gradle 对应版本是 3.3.3,升级到最新的 7.0.4

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
classpath 'com.android.tools.build:gradle:7.0.4'

Q2: distributionUrl 不匹配

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Caused by: org.gradle.api.internal.plugins.PluginApplicationException: Failed to apply plugin [id 'com.android.internal.version-check']
    at com.android.build.gradle.BasePlugin.apply(BasePlugin.kt:33)
    at com.android.build.gradle.LibraryPlugin.apply(LibraryPlugin.kt:26)
    at build_21d4k8dpcp55f408j9ar3yifm.run(/Users/user/Documents/workspace/App/adlibrary/build.gradle:1)
Caused by: java.lang.RuntimeException: Minimum supported Gradle version is 7.0.2. Current version is 6.1.1. If using the gradle wrapper, try editing the distributionUrl in /Users/user/Documents/workspace/App/gradle/wrapper/gradle-wrapper.properties to gradle-7.0.2-all.zip
    at com.android.build.gradle.internal.plugins.VersionCheckPlugin.apply(VersionCheckPlugin.kt:59)
    at com.android.build.gradle.internal.plugins.VersionCheckPlugin.apply(VersionCheckPlugin.kt:33)
    ...

A2: 升级 Android 12 对应 distributionUrl Gradle 版本

和尚将本地 gradle-wrapper.properties 中升级到与 classpath 一致的 7.0.2-all 即可;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip

Q3: Marven 仓库不支持 Http

和尚历史项目中引入了很多公司内部仓库和部分非 Https 的线上库,在 Gradle 升级后,Marven 仓库建议使用 Https 安全的仓库;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Could not resolve all dependencies for configuration ':classpath'.
   > Using insecure protocols with repositories, without explicit opt-in, is unsupported. Switch Maven repository 'maven(http://0.0.0.0:80/xxx/App)' to redirect to a secure protocol (like HTTPS) or allow insecure protocols. 
   See https://docs.gradle.org/7.0.2/dsl/org.gradle.api.artifacts.repositories.UrlArtifactRepository.html#org.gradle.api.artifacts.repositories.UrlArtifactRepository:allowInsecureProtocol for more details. 

A3: 升级 Https 线上库或解决安全警告

对于部分线上 Marven 仓库可以更新至 Https,对于不可更新的库可以通过添加 allowInsecureProtocol 属性解决 Gradle 仓库地址的不安全警告;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
repositories {
    maven {
      url "http://0.0.0.0:80/xxx/App"
      allowInsecureProtocol = true
    }
}

Q4: compile 弃用

和尚的历史项目中有个别 Module 中未及时修改 compile(),而 Gradle 升级之后已完全弃用 compile()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
A problem occurred evaluating project ':lib'.
> Could not find method compile() for arguments [directory 'libs'] on object of type
    org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.

A4: 更新为 api 或 implementation

根据具体的业务需求将 compile() 更新为 api / implementation 即可;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
api fileTree(dir: 'libs', include: ['*.jar'])

Q5: Android 工程依赖的 Java 版本过低

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
A problem occurred evaluating project ':lib'.
> Failed to apply plugin 'com.android.internal.library'.
   > Android Gradle plugin requires Java 11 to run. You are currently using Java 1.8.
     You can try some of the following options:
       - changing the IDE settings.
       - changing the JAVA_HOME environment variable.
       - changing `org.gradle.java.home` in `gradle.properties`.

A5: 更新 Android 依赖版本为 jdk 11.0.13

通过 AndroidStudio -> Preferences… -> Gradle 更新 jdk 版本即可;

Q6: AGCPluginTask 中 randomEncryptComponent 属性不应使用 @Optional 进行注释

和尚的历史项目中使用了 Huawei HMS 推送等,使用的 Marven 库版本较低,与升级后的 Gradle 不兼容;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':push:processDebugManifest'.
> A failure occurred while executing com.android.build.gradle.tasks.ProcessLibraryManifest$ProcessLibWorkAction
   > Manifest merger failed with multiple errors, see logs

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
==============================================================================

2: Task failed with an exception.
-----------
* What went wrong:
A problem was found with the configuration of task ':app:processDebugAGCPlugin' (type 'AGCPluginTask').
  - Type 'com.huawei.agconnect.agcp.AGCPluginTask' property 'randomEncryptComponent' of type boolean shouldn't be annotated with @Optional.

    Reason: Properties of primitive type cannot be optional.

    Possible solutions:
      1. Remove the @Optional annotation.
      2. Use the java.lang.Boolean type instead.

    Please refer to https://docs.gradle.org/7.0.2/userguide/validation_problems.html#cannot_use_optional_on_primitive_types for more details about this problem.

A6: 升级华为 HMS 库版本

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
classpath 'com.huawei.agconnect:agcp:1.5.2.300'

Q7: 使用 Intent 过滤器的 Service 需设置 exported 属性

此元素设置 Activity 是否可由其他应用的组件启动 —“true” 表示可以,“false” 表示不可以。若为 “false”,则 Activity 只能由同一应用的组件或使用同一用户 ID 的不同应用启动。 如果您使用的是 Intent 过滤器,则不应将此元素设置为 “false”。否则,在应用尝试调用 Activity 时,系统会抛出 ActivityNotFoundException 异常。相反,您不应为其设置 Intent 过滤器,以免其他应用调用 Activity

如果没有 Intent 过滤器,则此元素的默认值为 “false”。如果您将元素设置为 “true”,则任何知道其确切类名的应用均可访问 Activity,但在系统尝试匹配隐式 Intent 时,该 Activity 无法解析;

此属性并非是限制 Activity 向其他应用公开的唯一方式。您还可使用权限来限制哪些外部实体能够调用 Activity

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/.../src/main/AndroidManifest.xml Error:
    android:exported needs to be explicitly specified for <service>. 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. 
    See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.
/.../src/main/AndroidManifest.xml Error:
    android:exported needs to be explicitly specified for <service>. 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. 
    See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.

A7: 在所有 Module 中找到使用 Intent 过滤器的 Service 并按业务需求添加对应的 exported 属性

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<service
    android:name="com.xxx.app.push.OPushMessageService"
    android:permission="com.coloros.mcs.permission.SEND_MCS_MESSAGE"
    android:exported="true">
  <intent-filter>
    <action android:name="com.coloros.mcs.action.RECEIVE_MCS_MESSAGE" />
  </intent-filter>
</service>

Q8: 使用 Intent 过滤器的 Receiver 需设置 exported 属性

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/.../src/main/AndroidManifest.xml Error:
    android:exported needs to be explicitly specified for <receiver>. 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. See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.
/.../src/main/AndroidManifest.xml Error:
    android:exported needs to be explicitly specified for <receiver>. 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. See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.

A8: 在所有 Module 中找到使用 Intent 过滤器的 Receiver 并按业务需求添加对应的 exported 属性

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<receiver android:name="com.xxx.app.SystemReceiver"
    android:exported="false">
  <intent-filter>
    <action android:name="android.intent.action.BOOT_COMPLETED" />
    <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    <action android:name="android.intent.action.USER_PRESENT" />
  </intent-filter>
  <intent-filter>
    <action android:name="android.intent.action.PACKAGE_REMOVED" />

    <data android:scheme="package" />
  </intent-filter>
</receiver>

Q9: 使用 Intent 过滤器的 Activity 需设置 exported 属性

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/.../src/main/AndroidManifest.xml Error:
    android:exported needs to be explicitly specified for <activity>. 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. See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.
/.../src/main/AndroidManifest.xml Error:
    android:exported needs to be explicitly specified for <activity>. 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. See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.

A9: 在所有 Module 中找到使用 Intent 过滤器的 Activity 并按业务需求添加对应的 exported 属性

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<activity
    android:name=".xxx.app.TestActivity"
    android:exported="false"
    android:theme="@style/Theme.notAnimation">
  <intent-filter>
    <action android:name="com.sogou.novel.reader.setting.clean" />

    <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
</activity>

Q10: PendingIntent 需声明可变性

Android 12 中创建 PendingIntent 的时候,需要显示的声明是否可变,请分别使用 PendingIntent.FLAG_MUTABLEPendingIntent.FLAG_IMMUTABLE 标志,如果您的应用试图在不设置任何可变标志的情况下创建 PendingIntent 对象,系统会抛出 IllegalArgumentException 异常;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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.

A10: 根据业务设置 PendingIntent 可变性

PendingIntent 是一个可以给另一个应用程序使用的 IntentPendingIntent 接收待处理意图的应用程序可以使用与产生待处理意图的应用程序相同的权限和身份执行待处理意图中指定的操作;

Adnroid 12 之前,默认创建一个 PendingIntent 它是可变的,因此其他恶意应用程序可能会拦截,重定向或修改此 Intent

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
PendingIntent pendingIntent = PendingIntent.getBroadcast(getContext().getApplicationContext(), type, intent, PendingIntent.FLAG_IMMUTABLE);

W11: 避免使用 flatDirs 提醒

Gradle 升级之后,提示避免使用 flatDir 提醒,因该方式不支持任何元数据方式;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Using flatDir should be avoided because it doesn't support any meta-data formats.
Using flatDir2 should be avoided because it doesn't support any meta-data formats.

A11: 使用 jniLibs.srcDirs 方式引入 libs 库

Gradle 升级之后使用 jniLibs.srcDirs 方式替代 flatDirlibs 库引入,并更新 aar 引入方式;

当前版本
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
repositories {
    flatDir {
        dirs 'libs'
    }
}

implementation(name: 'test_name', ext: 'aar')
升级后版本
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
android {
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
}

implementation (files("libs/test_name.aar"))

W12: dexOptions 弃用提醒

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DSL element 'dexOptions' is obsolete and should be removed.
It will be removed in version 8.0 of the Android Gradle plugin.
Using it has no effect, and the AndroidGradle plugin optimizes dexing automatically.

A12: Gradle 升级后 dexOptions 已弃用,删除即可

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
dexOptions {
    preDexLibraries = true
}

Tips:

和尚在测试过程中,明明代码中所有涉及 intent-filter 过滤器的 Activity / Service / Receiver 都已经设置了 exported 属性,但依旧提示使用 Intent 过滤器的 XX 需设置 exported 属性;其原因在于引入了各类三方 SDK,在引入的各类三方库中可以存在对应的未设置 exported 属性的 Activity / Service / Receiver,单独设置处理一下即可;


Android 12 的初步升级到此位置,还有很多特有的属性,和尚会在后续的适配中进行完善;如有错误,请多多指导!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 阿策小和尚 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Android 12 自动适配 exported 深入解析避坑
众所周知,从 Android 12 开始,使用了 TargetSDK 31 之后,四大组件如果使用了 intent-filter, 但是没显性质配置 exported App 将会无法安装,甚至编译不通过。
GSYTech
2022/03/29
1.6K0
Android 12 自动适配 exported 深入解析避坑
写了一个适配 Android12-exported 的小插件
从 Android12 开始,如果我们的 tagSdk >=31, 即以 Android 12 或更高版本为目标平台时,且包含使用 intent 过滤器的 activity、服务或广播接收器,则必须为这些应用组件显式声明 android:exported 属性。
Petterp
2022/06/30
8530
写了一个适配 Android12-exported 的小插件
Android 12 快速适配要点
Android 12 需要更新适配点并不多,本篇主要介绍最常见的两个需要适配的点:android:exported 和 SplashScreen 。
GSYTech
2021/12/13
1.1K0
Android 12 快速适配要点
Android开发(21) Android Manifest.xml 结构详解二
AndroidManifest.xml配置文件对于Android应用开发来说是非常重要的基础知识,本文旨在总结该配置文件中重点的用法,
Java架构师必看
2021/05/27
7880
Android 12 适配攻略
2022年2月11日,谷歌发布了首个 Android 13 开发者预览版。2022年7月,发布了Beta 4版本,接下来就是Final Relase版本了。是时候适配一波Android12了,为后面项目适配铺平道路。
八归少年
2022/09/28
3.3K0
Android 12 适配攻略
App和SDK开发必看 | 个推分享Android12适配指南
10 月 4 日,谷歌将Android12源代码推送至 Android 开源项目 (AOSP)。自从2021年2月发布Android12第一个预览版以来,历经9个月时间测试和优化,正式版本的Android12终于来了!不仅在UI方面做了不少升级,Android12对个人隐私安全的保护也得到了进一步增强。整体来讲,Android12更加智能、高效和安全,感兴趣的开发者可以登录官网下载源码测试学习。
个推君
2021/12/24
1.6K0
App和SDK开发必看 | 个推分享Android12适配指南
android之注册registerReceiver
广播作为四大组件之一,使用方式也是多种多样的,既可以自己在manifest中注册,也可以在java代码中动态注册,既可以接收由系统发出的广播,也可以接受自己定义并发送的广播。
李小白是一只喵
2021/01/29
2.2K0
Android组件安全
组件是一个Android程序至关重要的构建模块。Android有四种不同的应用程序组件:Activity、Service、Content Provider和Broadcast receiver。组件的安全对于android应用来说不容忽视,下面介绍常用的android组件安全的测试方法。
信安之路
2018/08/01
2.4K1
Android组件安全
添加Metasploit-payload到已有的Android项目中
*本文原创作者:xiaohuanshu,本文属FreeBuf原创奖励计划,未经许可禁止转载 metasploit在写这篇文章之前,笔者可以说是对java一窍不通,也从来没有写过什么Android应用,在几天的摸爬滚打中终于实现了最终的目的,就是在已有Apk源码的情况下,用了比较另类的方式,添加metasploit后门。 同时支持java/scala两种语言写的项目,主要是为了给大家提供一些思路,如果哪里写的不准确,欢迎指正。 可能有人说,目前已经有各种各样的工具,比如backdoor-apk能够感染已编
FB客服
2018/02/09
1.3K0
添加Metasploit-payload到已有的Android项目中
安卓应用安全指南 4.1.2 创建/使用活动 规则书
仅在单个应用中使用的活动,不需要能够从其他应用接收任何意图。 开发人员经常假设,应该是私有的活动不会受到攻击,但有必要将这些活动显式设置为私有,以阻止恶意内容被收到。
ApacheCN_飞龙
2022/12/01
1.4K0
Android12,你的适配开始了吗
Android 12 开发者预览版计划从 2021 年 2 月开始启动,到向 AOSP 和 OEM 提供最终的公开版本时结束,目前应该已经到beta3阶段了,看看官方里程碑时间轴,你的适配工作开始了吗
Android扫地僧
2021/04/26
4.4K0
Android12,你的适配开始了吗
安卓应用安全指南 4.1.3 创建/使用活动 高级话题
我们已经解释了如何实现本指南中的四类活动:私有活动,公共活动,伙伴活动和内部活动。 下表中定义了每种类型的导出属性的允许的设置,和intent-filter元素的各种组合,它们在AndroidManifest.xml文件中定义。 请使用你尝试创建的活动,验证导出属性和intent-filter元素的兼容性。
ApacheCN_飞龙
2022/12/01
1.4K0
Android 组件逻辑漏洞漫谈
随着社会越来越重视安全性,各种防御性编程或者漏洞缓解措施逐渐被加到了操作系统中,比如代码签名、指针签名、地址随机化、隔离堆等等,许多常见的内存破坏漏洞在这些缓解措施之下往往很难进行稳定的利用。因此,攻击者们的目光也逐渐更多地投入到逻辑漏洞上。逻辑漏洞通常具有很好的稳定性,不用受到风水的影响;但同时也隐藏得较深、混迹在大量业务代码中难以发现。而且由于形式各异,不太具有通用性,从投入产出比的角度来看可能不是一个高优先级的研究方向。但无论如何,这都始终是一个值得关注的攻击面。因此,本文就以 Android 平台为目标介绍一些常见的逻辑漏洞。
evilpan
2023/02/12
1.9K0
Android 组件逻辑漏洞漫谈
【Android 电量优化】电量优化 ( 使用 AlarmManager 保持 CPU 唤醒 )
上一篇博客 【Android 电量优化】电量优化 ( 唤醒锁定 | 使用 WeakLock 保持服务唤醒 | 屏幕唤醒 ) 中 , 使用 WeakLock 保持 CPU 唤醒 , 全程 CPU 都处于工作状态 , 该操作耗电量非常大 ;
韩曙亮
2023/03/28
1.1K0
关于在Android 12的设备上安装app报错的问题
初步判断是AndroidManifest.xml文件内容有问题,经检查和询问Google,网络上有记录的引发此类问题的原因有以下几种。
Ant丶
2023/12/01
8810
行为变更 | 了解 Android 12 中的 intent-filter
Android 12 一个重要的变更是提高应用和系统的安全性,这个变更影响了所有目标版本为 Android 12 的应用。
Android 开发者
2022/03/09
1.9K0
Android 进阶3:Intent 与 IntentFilter 匹配规则
张拭心 shixinzhang
2018/01/05
1.1K0
Android 进阶3:Intent 与 IntentFilter 匹配规则
个推解读Android13,发布《Android13适配指南》
8月16日,谷歌宣布Android13新系统的源代码已经上传到Android开源项目(AOSP)中,Android13正式发布。自从2022年2月Android13第一个预览版上线以来,历经7个月的测试和优化,正式版本的Android13终于来了!Android13仍然聚焦个人隐私保护和安全,并提供了万物互联时代下大小屏适配、电池利用率优化等相关的技术开发能力。
个推
2022/09/05
2.5K0
个推解读Android13,发布《Android13适配指南》
AndroidManifest.xml常用标签属性介绍
AndroidManifest.xml常用标签属性介绍 manifest 标签 uses-permission 标签 uses-sdk 标签 application 标签 activity 标签 intent-filter 标签 action 标签 category 标签 data 标签 provider 标签 service 标签 receiver 标签 intent-filter 标签 action 标签 meta-data 标签 manifest 标签 manifest 标签 说明:根节点,描述了
是阿超
2022/05/29
1.5K0
全面解读系统更新,收藏下这份 Android 12 (S) 版本适配自查表
Android 12 是 2021 年 10 月发布的最新正式版本,然而很多同学表示还没有适配。针对开发者在进行版本适配过程中遇到的问题,我们建立了 GitHub · AndroidPlatformWiki[1]。我们希望站在开发者的视角,全面且深刻地解读每个 Android 版本更新,以此建立起一个体系化的 Android 系统适配手册。具体包括:
用户9995743
2022/09/26
3.1K0
全面解读系统更新,收藏下这份 Android 12 (S) 版本适配自查表
相关推荐
Android 12 自动适配 exported 深入解析避坑
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验