1. 项目概述:一个Android开发者的“瑞士军刀”仓库
如果你在GitHub上搜索过Android相关的开源项目,大概率会看到过rasy007/android-skills这个仓库。我第一次点进去的时候,感觉就像是走进了一个经验丰富的Android工程师的私人工具箱。这个项目不是一个单一的库或者框架,而是一个精心整理的、关于Android开发“技能”的集合。它没有复杂的构建脚本,也没有需要集成的依赖,它的核心价值在于内容本身——一份涵盖了从基础到进阶,从原理到实践的Android开发知识图谱。
这个仓库的名字“android-skills”非常直白,它就是一个关于Android开发技能的清单。但它的特别之处在于,它不是一份枯燥的教科书目录,而是更像一位同行在多年踩坑、填坑后,为你梳理出来的一条高效学习与实践路径。里面包含了面试准备、性能优化、架构设计、新特性(如Jetpack Compose)、甚至是一些底层原理的探讨链接和笔记。对于中级开发者来说,它是查漏补缺的宝典;对于初学者,它是一份极佳的学习路线图,告诉你除了写界面和调用API,一个合格的Android开发者还需要关注什么。
我自己的体会是,在职业生涯的不同阶段回顾这个仓库,总能有新的收获。早期可能更关注里面的面试题和基础概念;当开始负责模块设计时,会反复琢磨里面关于架构和设计模式的部分;等到需要处理线上性能问题时,里面的内存优化、启动优化等内容又成了救命稻草。接下来,我就结合这个仓库的脉络和我自己的实践经验,把它拆解成一份可以跟着学习和实践的“行动指南”。
2. 核心内容架构与学习路径解析
2.1 知识体系的模块化构成
android-skills仓库的内容组织虽然可能随着维护者的更新而变化,但其核心模块通常围绕以下几个支柱展开,这也是现代Android开发能力模型的缩影:
基础与语言:这是地基。包括Java/Kotlin的核心语法、面向对象思想、集合框架、并发编程(多线程与协程)、JVM基础等。很多开发者觉得业务代码用不到这些就忽视了,但恰恰是这些基础决定了你代码的上限和排查问题的深度。比如,不理解
Handler、Looper、MessageQueue的工作原理,就很难优雅地处理线程间通信,也无法深刻理解ANR的成因。Android框架层:这是Android开发的专属领域。包括四大组件(
Activity,Service,BroadcastReceiver,ContentProvider)的生命周期和通信机制、View系统与事件分发、Intent机制、资源与配置管理、数据存储(SharedPreferences,SQLite,Room)等。这一层知识直接决定了你能否正确地使用Android平台提供的能力。性能优化专题:这是区分普通开发者和资深开发者的关键分水岭。它不是一个孤立的知识点,而是贯穿于整个开发流程的意识和实践。通常包括:
- 启动优化:从点击图标到首帧绘制,每个阶段的耗时分析与优化手段(冷启动、温启动、热启动)。
- 内存优化:避免内存泄漏(
LeakCanary工具的使用)、识别并优化内存抖动、大图加载策略、Bitmap复用等。 - 布局与渲染优化:过度绘制检测、
View层级扁平化、使用ConstraintLayout、避免在onDraw中创建对象等。 - 网络优化:图片压缩、数据缓存策略、请求合并与减少、使用HTTP/2等。
- 耗电优化:后台任务管理、
JobScheduler、WorkManager、AlarmManager的合理使用,唤醒锁的谨慎控制。
架构与设计模式:随着应用复杂度提升,如何组织代码变得至关重要。这里涉及MVC、MVP、MVVM、MVI等架构模式的对比与选型,以及依赖注入(Dagger/Hilt)、单向数据流、响应式编程等思想。核心目标是实现关注点分离、提高可测试性和可维护性。
现代开发工具链:工欲善其事,必先利其器。包括Gradle构建系统的理解与优化(模块化、构建变体、依赖管理)、持续集成/持续部署(CI/CD)流程、静态代码分析工具(如
ktlint,Detekt)、以及Profiler、Layout Inspector等Android Studio自带的神器。拓展与前沿:保持技术敏感度。例如对Jetpack Compose声明式UI的学习,对Kotlin Multiplatform Mobile(KMM)的探索,以及对安全性、无障碍功能、动态化等领域的关注。
2.2 如何高效利用此类技能仓库
面对这样一个信息密集的仓库,切忌陷入“收藏即学会”的错觉。我推荐一种“三轮学习法”:
- 第一轮:通读与建立地图。快速浏览所有目录和主要条目,不追求理解每个细节,目标是建立一个宏观的认知地图,知道Android开发的技能树有哪些主要枝干。标记出自己完全陌生、一知半解和已经掌握的部分。
- 第二轮:专题深入与实践。结合当前工作中遇到的实际问题或个人的薄弱环节,选择一个专题(如“内存优化”)进行深度学习。不仅看仓库里的要点,更要根据其中的关键词(如“LeakCanary”、“MAT”)去搜索官方文档、阅读源码、撰写Demo验证,并尝试应用到实际项目中。这是将知识转化为技能的唯一途径。
- 第三轮:复盘与输出。在实践后,将自己的理解、踩过的坑、优化的成果整理成自己的笔记或博客。这个过程能极大地加深理解,并且,像
android-skills这样的仓库最初也是由开发者这样一点点积累而成的。输出是最好的输入。
注意:开源仓库的内容可能存在时效性。Android技术栈更新很快,对于仓库中提到的具体库(如
RxJava2.x到3.x)或API(如AsyncTask已废弃),一定要核对最新的官方文档(developer.android.com)和库的官方版本,切勿直接照搬过时方案。
3. 从理论到实践:深度剖析关键技能点
3.1 内存优化实战:从原理到工具链
内存问题,尤其是内存泄漏,是Android开发中最常见也最棘手的问题之一。android-skills里肯定会提到它,但我想结合自己的经历,把它讲得更透一些。
原理层面:首先要理解Java/ Kotlin在Android上的内存管理基本规则——可达性分析。一个对象被GC回收的条件是:从GC Roots对象(如静态变量、活动线程栈中的局部变量表等)出发,没有任何引用链可以到达该对象。内存泄漏的本质就是,一个对象已经不再被使用(业务逻辑上),但由于某些疏忽,它仍然被GC Roots或一条引用链可达,导致无法被回收,从而白白占用内存。
最常见的泄漏场景及应对:
- 非静态内部类/匿名内部类持有外部类引用:例如,在
Activity中创建一个Handler的非静态内部类,这个Handler会隐式持有Activity的引用。如果Handler被发送了延迟消息,或者其消息被其他长生命周期对象引用,就会导致Activity无法被及时回收。- 解决方案:将内部类声明为
static(静态内部类),并通过弱引用(WeakReference)来持有对外部类对象的引用。
- 解决方案:将内部类声明为
- 单例模式持有Context:如果单例类持有了一个
Activity的Context,那么这个Activity退出时,由于单例的生命周期与应用一致,Activity实例就无法被释放。- 解决方案:单例中尽量使用
Application Context(getApplicationContext())。如果必须使用Activity Context,确保在适当的时候(如Activity的onDestroy)解除引用。
- 解决方案:单例中尽量使用
- 资源性对象未关闭:
Cursor、File流、Bitmap等在使用后未调用close()或recycle()方法。- 解决方案:使用
try-with-resources(Java)或use函数(Kotlin)确保自动关闭。
- 解决方案:使用
- 监听器/广播未注销:在
Activity或Fragment中注册了系统服务(如SensorManager)的监听器,或者注册了全局/本地广播,在销毁时未注销。- 解决方案:在
onDestroy或对应的生命周期回调中,对称地进行注销操作。
- 解决方案:在
工具链使用心得:
- LeakCanary:这是必装的“内存泄漏检测神器”。集成后,它会在发生内存泄漏时在通知栏弹出提示,并生成详细的泄漏轨迹报告。关键技巧:不要只看它报出的最终持有者,要顺着引用链向上看,找到那个本应被释放却未被释放的业务对象(通常是你的
Activity或Fragment),然后分析为什么它还被引用着。LeakCanary 2.0以后的自定义配置也很强大,可以过滤一些已知的、无害的泄漏(如系统资源的延迟释放)。 - Android Profiler:Android Studio自带的性能分析工具。它的内存分析器可以实时看到堆内存的分配和回收情况,捕获堆转储(Heap Dump)。实操步骤:在怀疑有泄漏的场景操作你的App,然后触发一次GC,观察堆内存是否回落。如果不回落,抓取堆转储,在转储文件中,你可以按类排序,查看你的
Activity或Fragment类的实例个数,如果超过了预期(例如,已经退出多个页面,但实例数却很多),就很可能存在泄漏。再结合“引用”视图,一步步追踪是谁持有了它。 - MAT (Memory Analyzer Tool)或YourKit:更强大的离线堆转储分析工具,适合分析复杂的内存问题,可以计算对象保留集(Retained Set),精确看到某个对象到底“保住”了多少内存。
3.2 启动速度优化:打造“秒开”体验
应用启动速度是用户对应用的第一印象,也是衡量性能的关键指标。优化启动速度是一个系统工程。
启动阶段分解:通常分为三个阶段。
- 冷启动:应用进程不存在,系统需要创建进程并初始化应用。这是最耗时、最完整的启动过程。耗时从
Application的attachBaseContext()开始,到首个Activity的onResume()结束。 - 温启动:应用进程存在,但
Activity已被销毁。系统需要重建Activity。耗时从Activity的onCreate()开始。 - 热启动:应用进程和
Activity都在后台。系统只是将Activity带回前台。速度最快。
我们的优化重点在冷启动。
核心优化手段:
- 优化Application初始化:这是冷启动的“头号杀手”。检查你的
Application类以及其中初始化的第三方库(如统计分析、推送、地图等)。很多库都要求你在Application的onCreate中调用初始化方法。- 策略:区分关键初始化(应用运行必须的,如崩溃收集、基础工具类)和非关键初始化(可以延迟的,如某些第三方SDK)。对于非关键初始化,可以放到后台线程执行,或者延迟到真正需要使用时再初始化。可以使用
Jetpack Startup库来统一管理和优化初始化顺序。
- 策略:区分关键初始化(应用运行必须的,如崩溃收集、基础工具类)和非关键初始化(可以延迟的,如某些第三方SDK)。对于非关键初始化,可以放到后台线程执行,或者延迟到真正需要使用时再初始化。可以使用
- 优化首屏Activity的布局与加载:
- 布局优化:减少首屏
View层级和数量,优先使用ConstraintLayout减少嵌套。使用<include>、<merge>、<ViewStub>标签延迟加载非首屏必要视图。 - 主线程避堵:确保
onCreate、onStart、onResume中不执行任何耗时操作(如网络请求、大量数据库读写、复杂计算)。所有耗时任务必须异步执行。
- 布局优化:减少首屏
- 使用启动主题(Splash Theme)避免白屏/黑屏:在应用真正初始化完成前,系统会先显示一个空白窗口(WindowBackground)。我们可以通过给启动
Activity设置一个特殊的主题,将这个窗口的背景设置为一张与启动图类似的图片,给用户一种“瞬间启动”的错觉。- 实操:定义一个主题,将其
windowBackground属性设置为一张背景图(.9图最佳),并将启动Activity的theme设置为它。在Activity的onCreate的super.onCreate之前,再通过setTheme(R.style.AppTheme)将主题切换回正常的应用主题。这样视觉过渡会非常平滑。
- 实操:定义一个主题,将其
- 监控与度量:优化离不开数据。使用
adb shell am start -W [packageName]/[activityName]命令可以粗略测量启动时间。更精细的做法是在代码中打点,记录各个关键阶段(Application#attachBaseContext,Application#onCreate,Activity#onCreate等)的时间戳,上报到监控平台,持续追踪。
一个常见的坑:过度使用“启动主题”技巧,却忽略了真正的初始化耗时,导致用户点击图标后看到启动图,但长时间无法操作,体验更差。优化启动速度的本质是减少主线程的阻塞时间,视觉技巧只是辅助。
3.3 现代架构模式选型:MVVM与MVI的思考
android-skills中肯定会对比各种架构模式。目前,MVVM(Model-View-ViewModel)配合Jetpack组件已是Android官方推荐的主流。但近年来,MVI(Model-View-Intent)模式也开始受到关注。这里谈谈我的理解。
MVVM(以Jetpack为例)的核心:
- Model:数据层,负责数据的获取和业务逻辑(如Repository, DataSource)。
- View:UI层,通常是
Activity/Fragment或Compose函数,负责显示数据和接收用户输入。 - ViewModel:中间层,持有与UI相关的数据,并暴露给
View观察。它不持有View的引用,因此生命周期独立于View,配置变更时数据不会丢失。 - 数据绑定:通过
LiveData或StateFlow/SharedFlow,View观察ViewModel中的数据变化,自动更新UI。用户操作通过ViewModel暴露的方法来改变数据。
MVVM的优势:职责分离清晰,ViewModel可测试性强,配合DataBinding或ViewBinding可以减少样板代码。LiveData具有生命周期感知能力,自动管理订阅,避免内存泄漏。
MVVM的挑战:随着UI状态复杂化(例如一个页面有加载、成功、失败、空数据等多种状态),ViewModel中可能定义多个LiveData,状态更新分散,容易遗漏同步更新,导致UI状态不一致。
MVI的引入:MVI通过“单向数据流”和“唯一可信数据源”来应对状态复杂性问题。
- Intent:代表用户的“意图”(如
LoadDataIntent,SubmitFormIntent),是View发送给Model的唯一信号。 - Model:代表“状态”(
State),是描述UI在某一时刻所有信息的不可变数据类。 - View:接收一个唯一的
State流,根据State渲染UI。用户交互被转换为Intent发送。 - 单向流:
View发送Intent->Model(或称为Processor/Reducer)处理Intent,基于当前State和Intent计算出新State-> 新State推送给View更新。
MVI的优势:
- 状态可预测:所有状态变化都源于
Model对Intent的处理,逻辑集中,便于调试和复现问题。 - 不可变状态:
State是不可变的,每次更新都创建新对象,避免了多线程下的状态竞争问题。 - 易于测试:
Model是纯函数,输入(当前State + Intent)输出(新State)明确,单元测试极其简单。
如何选择?
- 对于大多数中低复杂度的业务场景,MVVM +
StateFlow完全够用,且学习成本低,生态成熟。你可以通过定义一个密封类(sealed class)来统一管理UI状态,模拟MVI的部分优点。 - 对于UI状态极其复杂、交互繁多、对状态一致性要求极高的页面(如一个复杂的表单、实时协作编辑),可以考虑引入MVI。可以使用
kotlinx.coroutines的Flow和StateFlow,或者直接使用现成的MVI框架如Orbit、Mobius等。
心得:架构没有银弹。不要为了追求时髦而强行使用MVI。MVVM的简单直接在很多场景下是最高效的。关键是理解“关注点分离”和“单向数据流”的思想,并根据项目团队规模和复杂度做出合适的选择。在大型项目中,甚至可以混合使用,在复杂模块用MVI,简单模块用MVVM。
4. 构建与工程化:超越业务代码的进阶能力
一个优秀的Android开发者不能只停留在写Activity和Adapter。工程化能力决定了项目的长期健康度和团队的开发效率。
4.1 Gradle构建优化实战
随着项目模块增多,构建速度可能成为开发流程的瓶颈。一次clean build动辄几分钟,严重打断开发心流。
常规优化手段:
- 开启构建缓存:在
gradle.properties中设置org.gradle.caching=true。Gradle会缓存任务的输出,下次构建时直接复用。 - 启用并行构建:在
gradle.properties中设置org.gradle.parallel=true。Gradle会并行执行独立的任务。 - 启用配置缓存(Configuration Cache):这是Gradle 6.6引入的实验性功能,可以缓存构建配置阶段的结果,对增量构建提速明显。在
gradle.properties中设置org.gradle.configuration-cache=true。注意:需要确保你的构建脚本和插件兼容此特性。 - 调整JVM堆大小:在
gradle.properties中设置org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m,根据机器内存适当增加。 - 使用产品风味(Flavors)和构建变体(Build Types)的维度管理:避免创建过多不必要的变体组合,每个变体都会增加构建任务。
进阶模块化与构建分析:
- 模块化:将项目拆分为多个Gradle模块(
library)。模块化本身可能会增加一次全量构建的时间,但它带来了巨大的好处:并行编译(模块间独立)、增量编译范围缩小、代码边界清晰、团队协作更方便。使用api和implementation正确声明依赖,避免传递依赖泄露。 - 使用构建分析报告:运行
./gradlew :app:assembleDebug --profile,Gradle会生成一个HTML格式的构建报告,详细列出每个任务的耗时。重点关注耗时最长的任务,分析原因。常见瓶颈:kapt(Kotlin注解处理)、资源合并、Dex等。 - 优化
kapt:如果使用了大量注解处理器(如Dagger、Room),kapt会非常慢。尝试:- 使用
kapt的增量处理(通常默认开启)。 - 对于Dagger,考虑迁移到Hilt,它简化了配置并可能带来性能提升。
- 对于Room,确保使用了
room.schemaLocation导出模式,避免每次编译都处理数据库历史。
- 使用
4.2 代码质量与静态分析
在团队协作中,保持代码风格一致和发现潜在问题至关重要,不能只依赖Code Review。
- ktlint/Detekt:这是Kotlin项目的标配。
ktlint专注于代码格式,Detekt功能更强大,可以进行复杂度分析、发现代码异味(Code Smell)。将它们集成到Gradle构建脚本中,在check或build任务前自动运行,不符合规则的代码会导致构建失败。- 配置:可以自定义规则集,关闭一些过于严格的检查。通常团队会共享一份配置文件。
- 自定义Lint规则:Android Lint非常强大,但默认规则可能不满足团队特定需求。例如,你可以编写自定义Lint规则来:禁止直接使用
android.util.Log(强制使用封装好的日志工具)、检查某些特定注解的使用方式、强制要求资源命名前缀等。虽然编写自定义Lint有一定门槛,但对于大型团队规范代码非常有效。 - SonarQube:这是一个更重量级的代码质量管理平台。它可以集成多种静态分析工具的结果,并提供长期的历史趋势、技术债务分析、新代码质量门禁等。适合在CI/CD流水线中集成,作为代码合并前的质量关卡。
实操心得:静态检查规则的引入要循序渐进。一开始可以只开启最关键的、无争议的规则(如未使用的导入、可能的空指针)。随着团队适应,再逐步加入更严格的规则(如复杂度限制、过长的函数)。切忌一开始就开启所有“严厉”规则,这会引起开发者的反感,导致规则被绕过或禁用。
5. 疑难问题排查与调试技巧实录
开发过程中,总会遇到一些“诡异”的问题。拥有系统的排查思路比记住具体答案更重要。
5.1 ANR(Application Not Responding)问题定位
ANR的直接原因是主线程被阻塞超过一定时间(通常5秒)。但找到“谁”阻塞了它,是关键。
标准排查流程:
- 获取 traces.txt 文件:发生ANR后,系统会在
/data/anr/目录下生成traces.txt文件。可以通过adb pull /data/anr/traces.txt .拉取到本地。在Android 11及以上版本,需要设备有root权限或使用adb bugreport命令获取。 - 分析 traces.txt:这个文件包含了发生ANR时所有线程的堆栈信息。首先找到主线程(通常是
main或包名相关的线程),看它的堆栈停留在哪里。常见的阻塞点:- 同步锁等待:堆栈显示在
wait()、lock()或synchronized代码块处。检查是否在主线程中尝试获取一个被子线程持有的锁。 - 耗时I/O操作:堆栈显示在文件读写、数据库操作(特别是未优化的大型查询)、网络请求(虽然现代网络库通常异步,但序列化/反序列化大数据也可能耗时)。
- Binder调用阻塞:调用系统服务(如
ContentResolver、TelephonyManager)时,如果系统服务端繁忙,主线程的Binder调用也可能被阻塞。
- 同步锁等待:堆栈显示在
- 使用
StrictMode进行预防:在开发阶段,可以在Application的onCreate中启用StrictMode,它会检测主线程中的磁盘读写和网络访问,并立即在Logcat中打印警告,帮助你在开发期就发现潜在问题。 - 监控与告警:在线上环境中,可以集成APM(应用性能监控)工具,如腾讯的Matrix、字节跳动的Rhea等,它们可以捕获ANR发生的现场信息(堆栈、设备状态等)并上报,方便线上问题追踪。
一个典型案例:曾经遇到一个ANR,traces.txt显示主线程卡在SharedPreferences的edit().apply()上。深入分析发现,apply()虽然是异步写入磁盘,但在Activity的onPause或onStop中,系统会等待所有apply()的写入完成,如果此时有大量apply()操作,就会阻塞主线程。解决方案:对于非即时性要求的配置保存,改用commit()(在子线程)或者控制apply()的调用频率。
5.2 界面卡顿与渲染性能优化
用户能直接感知的卡顿,通常是由于主线程在16.6ms(60Hz屏幕)内未能完成一帧的绘制任务。
诊断工具:
- Profile GPU Rendering:在开发者选项中开启“GPU渲染模式分析”或“Profile HWUI rendering”,屏幕上会显示彩色条形图。每条柱状线代表一帧,绿线是16.6ms的基准线。如果柱状图经常超过绿线,说明有卡顿。不同颜色段代表渲染的不同阶段(测量、布局、绘制等),可以帮助定位瓶颈。
- Systrace:这是更强大的系统级跟踪工具。它可以捕获一段时间内所有CPU核心、系统进程、应用线程的详细执行情况。通过
python systrace.py命令或Android Studio的Profiler中的System Trace功能生成报告。在报告里,你可以看到每一帧的详细时间消耗,精确到是哪个方法、哪个UI操作导致了超时。学习Systrace需要一定成本,但它是解决复杂性能问题的终极武器。 - Layout Inspector:检查视图层级是否过深,是否存在不必要的嵌套。
常见卡顿场景与优化:
- 布局层级过深与过度绘制:使用
ConstraintLayout扁平化布局。移除不必要的背景。通过开发者选项中的“显示过度绘制”来查看界面,蓝色为佳,出现大面积红色或粉色就需要优化。 onDraw中执行耗时操作或创建对象:onDraw会被频繁调用,必须高效。避免在这里创建新的Paint、Path等对象,应在初始化时创建并复用。避免进行复杂的计算。- 列表(RecyclerView)滑动卡顿:
- ViewHolder复用:确保正确实现。
- 布局优化:Item布局要简单。
- 异步加载与缓存:图片加载使用
Glide或Coil,它们自带强大的缓存和异步加载机制。对于复杂Item的绑定数据操作,确保不会阻塞主线程。 setHasFixedSize(true):如果RecyclerView的尺寸固定,设置此属性可以避免不必要的布局测量。- 差分更新:使用
ListAdapter或DiffUtil来更新数据,而不是notifyDataSetChanged(),后者会导致所有Item重新绑定。
5.3 兼容性问题的系统性处理
Android碎片化严重,处理兼容性问题是一项长期工作。
建立设备矩阵:不可能在所有设备上测试。需要根据市场占有率、分辨率、系统版本、厂商(特别是华为、小米、OPPO、vivo等国内厂商有其定制系统)等因素,建立一个有代表性的测试设备矩阵。
常见兼容性问题库:
- AndroidX AppCompat:始终使用最新的AppCompat库,它提供了大量向后兼容的UI组件和行为。
- Google的官方兼容库:如
Webkit提供现代WebView API的兼容。 - 第三方库:如
Blankj/AndroidUtilCode中的兼容性工具类。
特定问题处理:
- 权限管理:从Android 6.0 (API 23)开始的动态权限,到Android 10 (API 29)的作用域存储,再到Android 11 (API 30)的权限授予方式变化。必须使用
ActivityResult API(registerForActivityResult)来请求权限和获取结果,这是当前官方推荐且兼容性最好的方式。 - 后台限制:从Android 8.0 (API 26)的后台服务限制,到Android 10的后台活动启动限制,再到Android 12的精确闹钟权限。对于后台任务,统一使用
WorkManager是最佳实践,它能根据系统版本自动选择最合适的实现方式(JobScheduler,GcmNetworkManager,AlarmManager)。 - 厂商定制ROM:这是最头疼的。常见问题:自启动管理、电池优化白名单、通知渠道特殊行为、后台进程保活策略不同。应对策略:
- 引导用户手动设置:在应用内友好地提示用户去系统设置中为应用开启“自启动”、“允许后台运行”、“忽略电池优化”等选项(需跳转到对应的系统设置页面,各厂商页面路径不同,需要适配)。
- 加入厂商推送联盟:对于推送功能,优先集成各厂商的推送SDK(如小米推送、华为推送),在对应厂商设备上使用其系统级通道,可以极大提高推送到达率。
- 社区与测试:关注开发者社区,建立用户反馈渠道,针对高频问题定向适配。
处理兼容性问题的核心是“向前兼容,渐进增强”。使用最新的API和开发范式(如ViewBinding,Jetpack组件),同时利用支持库和条件判断,确保在旧版本上也能有可接受的体验。永远在最低支持版本的系统上进行核心功能的测试。