第10章:新人成长路线与资源推荐

学完基础只是起点 —— 这份路线图帮你从入门走向实战

🔬

费曼 (Richard Feynman)

教是最好的学

我这辈子最大的发现不是量子电动力学,而是一个学习方法:如果你没办法把一件事用简单的话解释清楚,那你根本就没搞懂它。编程也一样 —— 你以为你会了,但能不能合上书本,在白板上从零把它讲给一个外行听?这一章,我们来聊聊怎么学,怎么不骗自己,怎么真正变强。记住:第一原则是你绝不能欺骗自己 —— 而你恰恰是最容易被自己骗的人。

10.1 我们是怎么骗自己的 —— 常见踩坑总结

在物理学中,我们最大的敌人不是难题,而是自以为懂了的幻觉。编程也是一样。在学习 Kotlin 和 Android 的过程中,几乎每个人都会掉进同样的坑 —— 不是因为这些坑有多隐蔽,而是因为我们太善于骗自己了。"这段代码能跑就行了"、"这个问题以后再处理"、"我知道这里有风险但应该不会出事" —— 这些都是自我欺骗的经典台词。

下面这六个陷阱,就是开发者最常骗自己的六种方式。记住我的话:第一原则是你绝不能欺骗自己 —— 而你恰恰是最容易被自己骗的人。

自我欺骗 1:用 !! 让编译器闭嘴

Kotlin 有一套非常精妙的空安全系统 —— 编译器在帮你检查每一个可能为 null 的地方。然而很多新手做的第一件事就是:用 !! 堵住编译器的嘴。这就好比你的烟雾报警器响了,你不去检查是不是着火了,而是把电池拔了。你骗自己说"这里不可能是 null",但你怎么知道的?你真的验证过每一条路径吗?

// 自我欺骗:用 !! 强制解包,对编译器说"闭嘴,我知道"
val name: String? = getUserName()
val length = name!!.length  // 如果 name 为 null,直接崩溃!

// 诚实做法 1:安全调用操作符 ?.
val length = name?.length  // 如果 name 为 null,length 也为 null

// 诚实做法 2:Elvis 操作符 ?: 提供默认值
val length = name?.length ?: 0  // 如果 name 为 null,length 为 0

// 诚实做法 3:let 块中安全处理
name?.let { safeName ->
    println("名字长度: ${safeName.length}")
}
别骗自己

现在就在你的代码中搜索 !!,每一个出现的地方都是你骗自己的证据。经验法则:如果你在项目中使用了超过 5 个 !!,说明你在系统性地自我欺骗。编译器提醒你有风险,不是在为难你 —— 它比你更诚实。

自我欺骗 2:用 GlobalScope "省事"

这是另一种经典的自我欺骗:"用 GlobalScope 更简单嘛,反正能跑。"能跑不等于没问题。GlobalScope 启动的协程生命周期和整个应用一样长。当用户退出 Activity 后,这个协程还在后台偷偷运行,抓着已经"死掉"的 Activity 不放 —— 内存泄漏就是这么来的。你骗自己说"没关系,用户不会注意到",但内存泄漏积累起来,应用迟早会卡到崩溃。

// 自我欺骗:在 Activity 中使用 GlobalScope
class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        GlobalScope.launch {  // 危险!Activity 销毁后协程仍在运行
            val data = fetchDataFromNetwork()
            updateUI(data)  // Activity 已销毁,可能崩溃
        }
    }
}

// 诚实做法:使用 lifecycleScope
class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycleScope.launch {  // Activity 销毁时自动取消
            val data = fetchDataFromNetwork()
            updateUI(data)  // 安全,Activity 销毁后不会执行
        }
    }
}
协程作用域选择指南

在 Activity/Fragment 中使用 lifecycleScope,在 ViewModel 中使用 viewModelScope。只有在确实需要应用级别生命周期的场景(如后台数据同步)才考虑自定义的 CoroutineScope,几乎永远不要使用 GlobalScope

自我欺骗 3:"Compose 不就是写 UI 吗,能显示就行"

很多人觉得自己懂 Compose 了,因为"界面显示出来了"。但你真的理解重组(Recomposition)吗?Compose 的声明式 UI 会在状态变化时重新调用 Composable 函数。如果你不理解这个机制,就会在不知不觉中写出每一帧都在做无用功的代码。你骗自己说"反正手机性能够用",但用户能感受到那种说不出来的卡顿。

// 自我欺骗 1:在 Composable 中做耗时操作
@Composable
fun UserProfile(userId: String) {
    val user = loadUserFromDatabase(userId)  // 每次重组都会执行!
    Text(text = user.name)
}

// 诚实做法:使用 remember 缓存计算结果
@Composable
fun UserProfile(userId: String) {
    val user by remember(userId) {
        mutableStateOf(loadUserFromDatabase(userId))
    }
    Text(text = user.name)
}

// 自我欺骗 2:每次重组都创建新对象
@Composable
fun AnimatedButton() {
    val colors = ButtonDefaults.buttonColors(  // 每次重组都创建新对象
        containerColor = Color.Blue
    )
    Button(colors = colors) { Text("点击") }
}

// 诚实做法:用 remember 缓存
@Composable
fun AnimatedButton() {
    val colors = remember {
        ButtonDefaults.buttonColors(containerColor = Color.Blue)
    }
    Button(colors = colors) { Text("点击") }
}

自我欺骗 4:"依赖冲突?不管它,先跑起来再说"

当你引入多个第三方库时,它们可能依赖同一个库的不同版本。编译器报了一堆警告,你选择忽略 —— "反正编译通过了嘛"。这是典型的把问题扫到地毯下面。等到某天运行时莫名其妙崩了,你才发现问题早就在那里了,只是你不愿意面对。在科学中,忽视实验中的异常数据是最大的罪过。在编程中,忽视编译警告同样如此。

// 排查方法:在终端运行以下命令查看依赖树
./gradlew app:dependencies

// 解决方法 1:强制指定版本
configurations.all {
    resolutionStrategy {
        force("com.google.code.gson:gson:2.10.1")
    }
}

// 解决方法 2:排除冲突的传递依赖
implementation("com.some.library:1.0") {
    exclude(group = "com.google.code.gson", module = "gson")
}
消除自我欺骗的工具

使用 Version Cataloglibs.versions.toml)统一管理依赖版本是避免冲突的最佳实践。同时,使用 BOM(Bill of Materials)可以确保同一套库的版本兼容,比如 Compose BOMFirebase BOM。工具不会骗你 —— 善用它们。

自我欺骗 5:"Context 随便传一个就行了吧"

Android 中有多种 Context,你骗自己说"Context 就是 Context,随便用哪个都一样"。这就像说"电池就是电池,随便装哪个型号都一样" —— 有时候确实凑巧能用,但迟早会出问题。Application ContextActivity Context 有本质区别,搞混了轻则功能异常,重则内存泄漏。

// 自我欺骗:在长生命周期对象中持有 Activity Context
class DataManager(private val context: Context) {
    // 如果传入 Activity Context,会导致 Activity 无法被回收
}

// 诚实做法:长生命周期对象使用 Application Context
class DataManager(private val context: Context) {
    init {
        // 保存 Application Context,避免 Activity 泄漏
        val appContext = context.applicationContext
    }
}
操作 Application Context Activity Context
显示 Dialog 不可以 可以
启动 Activity 需要 FLAG_ACTIVITY_NEW_TASK 可以
获取资源/字符串 可以 可以
数据库/SharedPreferences 推荐 可以
长生命周期对象中持有 安全 危险(泄漏)

自我欺骗 6:"这个操作很快的,放主线程没问题"

这大概是最危险的自我欺骗了。你在自己的高配开发机上测试 —— "网络请求瞬间就返回了,放主线程也没事。"但你的用户可能在地铁里用着老旧手机和拥挤的网络。主线程被阻塞超过 5 秒,系统就会弹出 ANR(Application Not Responding)。你在用自己的理想环境欺骗自己,而不是面对用户的真实环境。

// 自我欺骗:在主线程进行网络请求
fun loadData() {
    val response = URL("https://api.example.com/data").readText()  // 主线程阻塞!
    textView.text = response
}

// 诚实做法:使用协程切换到 IO 线程
fun loadData() {
    lifecycleScope.launch {
        val response = withContext(Dispatchers.IO) {
            URL("https://api.example.com/data").readText()  // 在 IO 线程执行
        }
        textView.text = response  // 自动回到主线程更新 UI
    }
}
需要放在后台线程的操作

网络请求、文件读写、数据库操作、大量数据处理、图片编解码 —— 这些操作必须放在后台线程执行。如果你使用 Retrofit + 协程,网络请求会自动在后台线程执行;使用 Room 数据库时,挂起函数也会自动切换线程。不要用"应该没问题"来骗自己,用工具来保证没问题。

10.2 调试技巧与工具 —— 用实验验证,而不是用猜测欺骗自己

在物理学里,我们不会因为一个理论"看起来对"就接受它 —— 我们要做实验。编程也一样。你以为代码逻辑是对的?别猜,用调试工具去验证。写代码和调试代码的时间比通常是 1:1 甚至更高。与其坐在那里盯着屏幕想"这里应该没问题吧",不如学会高效地使用工具来证明或者否定你的假设。Android Studio 提供了一整套强大的实验工具,让我们逐一掌握它们。

Logcat:你的实验日志

就像科学家要记录每一次实验的数据一样,程序员也需要记录程序运行时的状态。Logcat 就是你的实验日志本。它显示设备或模拟器上所有应用的日志输出,帮你观察程序在每个时刻到底在做什么 —— 而不是你"以为"它在做什么。

// 不同级别的日志
Log.v("MyTag", "Verbose:详细信息,一般开发阶段使用")
Log.d("MyTag", "Debug:调试信息")
Log.i("MyTag", "Info:一般信息")
Log.w("MyTag", "Warning:警告信息")
Log.e("MyTag", "Error:错误信息")

过滤技巧 —— 好的科学家知道怎么从海量数据中找到关键信息:

让日志成为你的好习惯

为你的项目定义统一的 Tag 前缀,比如 "MyApp_",这样可以用 tag:MyApp_ 一次过滤出所有自己的日志。避免在生产代码中保留大量 Log.d 调用,可以封装一个日志工具类,在 Release 构建中自动禁用调试日志。好的实验记录是有条理的,你的日志也应该如此。

Debugger:暂停时间,审视真相

Logcat 像是看一部电影的字幕 —— 你能看到发生了什么,但不能暂停。断点调试器则让你拥有了"暂停时间"的超能力:在任何一个时刻冻结程序,仔细检查每一个变量的值。这就好比在实验的任意一步按下暂停键,用放大镜检查每一个数据点。

基本断点:在代码行号左侧点击即可添加断点(红色圆点)。程序运行到这一行时会暂停。

条件断点:右键点击断点,可以设置条件表达式。例如设置 i == 50,断点只在循环变量 i 等于 50 时才触发。你不需要在一万次循环中手动按"下一步"来找那个出问题的第 50 次迭代。

日志断点:右键点击断点,取消 "Suspend" 勾选,启用 "Log message to console"。程序不会暂停,但会在命中断点时输出日志。这相当于一个可以随时添加和移除的 Log 语句,不需要改一行代码。

调试快捷操作

Layout Inspector:用你的眼睛验证 UI

你以为你的布局是对的?别猜 —— 用 Layout Inspector 去看。这个工具可以在运行时查看应用的视图层级结构。对于 Jetpack Compose 应用,它能显示 Composable 的组合树、属性和重组次数。重组次数过高?这就是你的代码在浪费资源的实验证据。

使用方法:在 Android Studio 中选择 Tools > Layout Inspector,然后选择正在运行的应用进程。你可以:

Profiler:让数据替你说话

你觉得应用"挺流畅的"?这是主观感受,不是实验数据。Profiler 能给你硬邦邦的数字,告诉你哪些函数在消耗时间、哪些对象在占用内存、哪些网络请求在拖慢速度。在科学中,我们用数据说话,不用感觉。

CPU Profiler:记录方法调用和执行时间。可以用来找出哪些方法执行时间过长、哪些方法被过于频繁地调用。发现主线程上的耗时操作特别有用。

Memory Profiler:监控应用的内存使用情况。可以抓取堆转储(Heap Dump),分析对象的引用关系,发现内存泄漏。如果你看到内存使用持续上升且不回落,这就是铁证 —— 你的代码在泄漏内存。

Network Profiler:监控网络请求的时间线、请求体和响应体。可以查看每个请求的耗时、数据大小等信息,帮助优化网络性能。

Android Studio 实用快捷键

好的工具要用得顺手。就像实验室里的仪器摆放一样,快捷键能让你的手指不必在键盘和鼠标之间来回跑,把注意力集中在真正重要的事情上 —— 思考。

快捷键 (macOS / Windows) 功能
Cmd+Shift+F / Ctrl+Shift+F 全局搜索文本
Cmd+Shift+A / Ctrl+Shift+A 搜索所有操作/命令
Cmd+B / Ctrl+B 跳转到定义
Cmd+Option+L / Ctrl+Alt+L 格式化代码
Cmd+Shift+O / Ctrl+Shift+N 搜索文件
Cmd+E / Ctrl+E 最近打开的文件
Cmd+/ / Ctrl+/ 注释/取消注释当前行
Option+Enter / Alt+Enter 快速修复/意图操作
Shift+Shift 全局搜索(Search Everywhere)
投资回报率最高的学习

花一两天时间熟悉 Android Studio 的快捷键和工具,就像花时间学会正确使用实验仪器一样 —— 这笔投资的回报是惊人的。建议安装 Key Promoter X 插件,它会在你每次使用鼠标操作时提示对应的快捷键。让工具帮你形成好习惯。

10.3 学习方法与策略 —— 费曼学习法的真正精髓

让我跟你说个秘密:世界上并不存在"天才的学习方法"。学习的核心就一件事 —— 对自己诚实。你到底懂了没有?别用"好像懂了"来骗自己。下面这些策略,本质上都是在帮你做同一件事:发现自己到底在哪里还不懂。

费曼学习法:如果你不能简单地解释它,你就没有真正理解它

人们给它起了一个很响亮的名字叫"费曼学习法",但其实它简单得不能再简单了。步骤如下:

  1. 选一个概念:比如"Kotlin 的空安全系统"。
  2. 假装你要解释给一个完全不懂编程的人听:不用术语,不用行话。如果你需要用 null 这个词,你得先解释 null 是什么。
  3. 找到卡壳的地方:当你发现自己说不下去了、绕来绕去了、或者不自觉地开始用术语了 —— 恭喜你,你找到了自己没有真正理解的地方。
  4. 回到源头重新学:针对那个卡壳的点,重新看代码、跑实验、查资料。然后再来一遍。

这个方法的威力在于:它让你无法自我欺骗。你可以对自己说"我懂了",但当你要用大白话给别人讲的时候,那些你似懂非懂的地方会暴露得一清二楚。

试试看:学完协程后,不看代码,尝试在白板或纸上从零写出一个使用 viewModelScope.launch + withContext(Dispatchers.IO) 加载数据的完整流程。写不出来?那就说明你还需要回去再看。这没什么丢人的 —— 发现自己不懂才是学习的开始。

实践建议

找一个朋友(或者假想一个),每学完一个知识点就向他"讲课"。你可以写博客、录视频、或者就对着橡皮鸭讲。形式不重要,重要的是你要把知识从大脑中输出出来,而不是让它留在"好像懂了"的模糊状态中。教别人的过程中,你会学到的比你教给别人的还要多。

项目驱动学习:别等"准备好了"再开始

很多人说"我先把 Kotlin 学完再开始做项目"。这就像说"我先把流体力学学完再去游泳" —— 你永远不会觉得自己准备好了。最好的方式是:跳进去,然后边游边学。

  1. 确定一个小目标:比如"做一个能添加和删除待办事项的 App"
  2. 拆解需求:UI 怎么画?数据存在哪?状态怎么管理?
  3. 遇到什么学什么:需要列表就学 LazyColumn,需要存数据就学 Room
  4. 完成后复盘:哪些地方写得别扭?有没有更好的方式?

本教程的第 16 章提供了一个完整的待办事项 App 项目实战,你可以跟着一步步完成。但更好的做法是:先自己试着做,卡住了再看教程。只有亲自撞过墙,你才知道那堵墙在哪。

问对问题比找到答案更重要

在科学研究中,提出一个好问题比找到答案更难也更重要。学编程也是一样。当你遇到 bug 时,不要急着搜索错误信息。先问自己:

这个思考框架不仅能帮你更快地定位 bug,还能帮你更深入地理解系统的运行机制。每一个 bug 都是一次学习机会 —— 它在告诉你:你对这个系统的理解在某个地方是错的。找到那个"错的地方",你就进步了。

刻意练习:别在舒适区里打转

人类有一个天性:反复做自己已经会的事情,因为那会带来"我很厉害"的满足感。但这是另一种自我欺骗。如果你对空安全很熟练但协程总是写错,那你应该把大部分时间花在协程上,而不是继续刷空安全的题目来感觉良好。真正的成长发生在舒适区的边缘。

阅读优秀代码:站在巨人的肩膀上

我在加州理工教书的时候跟学生说过:如果你只看教科书,你只能学到教科书的水平。要想超越,你得去看大师们怎么做。阅读优秀的开源项目源码就是这个道理。

  1. 先看 README 了解项目整体结构
  2. build.gradle.kts 了解技术栈
  3. 从入口点(如 MainActivity)开始,沿着数据流逐步深入
  4. 关注你当前正在学习的技术点,跳过其他部分

Google 官方维护了多个示范级别的 Android 项目(如 Now in Android),展示了 Compose、协程、Hilt、Room 等技术的最佳实践。在 GitHub 上搜索 android 组织下的仓库即可找到。

别贪多,每次只看一个方面

不要试图一次看完整个项目。每次只关注一个方面:这次看它的架构分层,下次看它的状态管理,再下次看它的测试怎么写。这就像做实验 —— 每次只改变一个变量,这样你才能知道是什么导致了什么。

10.4 从入门到项目实战的路线图

有了前面章节的基础知识,你需要的是一份可执行的学习计划。但我要提醒你:路线图是地图,不是你的双脚。地图告诉你方向,但你得自己走每一步。下面这份路线图按阶段划分,每个阶段都有明确的学习目标和检验标准 —— 注意,检验标准不是"我感觉我懂了",而是具体的、可验证的行为。能做到,才算真的会了。

阶段 1:Kotlin 语言基础(1-2 周)

学习目标:掌握 Kotlin 核心语法,能够流畅地使用 Kotlin 编写逻辑代码。

对应章节

检验标准(别骗自己,动手试):能用 Kotlin 独立实现简单的算法题(排序、字符串处理等)。能用自己的话解释 valvar?.!!outin 的区别 —— 解释给一个不懂编程的人听。

阶段 2:Compose 基础(2-3 周)

学习目标:理解声明式 UI 的概念,能用 Compose 构建常见的 UI 界面。

对应章节

检验标准:能独立构建一个包含列表、详情页、表单输入的多页面应用界面(不需要后端数据,先用假数据)。关键词是"独立" —— 合上教程,从空白项目开始。

阶段 3:架构组件(2-3 周)

学习目标:掌握 Android 核心架构组件,能搭建规范的应用架构。

对应章节

检验标准:能在白板上画出 MVVM 架构的数据流图,并用大白话解释每一层的职责。能实现一个完整的"数据来源 -> Repository -> ViewModel -> UI"的数据流。如果画不出来或者解释不清楚,那就说明还需要回去重新理解。

阶段 4:第一个完整 App(3-4 周)

学习目标:独立完成一个功能完整、架构规范的 Android 应用。

对应章节

推荐项目选择(选一个即可):

技术要求

检验标准:应用能正常安装运行、无崩溃。代码有清晰的分层结构。最重要的一条:你能向其他人解释你的架构设计和技术选型 —— 不是背答案,是真的理解为什么这样做。

阶段 5:进阶与持续成长(持续)

学习目标:掌握生产级应用开发所需的进阶技能,具备独立负责完整项目的能力。

学习内容

检验标准:成功发布至少一个应用。项目有完整的自动化测试覆盖。能够在团队中进行 Code Review,并给出有建设性的反馈 —— 这要求你不仅知道"怎么做",还知道"为什么这样做"以及"为什么不那样做"。

关于时间安排

上述时间安排假设每天投入 2-3 小时学习。实际进度因人而异,重要的不是严格遵守时间表,而是确保每个阶段的检验标准都真正达到了再进入下一阶段。我见过太多人在基础没打好的情况下急着往前赶,结果到了后面越学越痛苦,因为地基是歪的。宁可慢一点学扎实,也不要赶进度留下知识空洞 —— 你骗不了编译器,也骗不了你自己的代码。

10.5 结语

到这里,你已经掌握了 Kotlin 和 Android 开发的基础知识,也有了一份清晰的成长路线图。

但这只是旅程的中点,不是终点 —— 后面的章节将带你深入更多进阶主题:泛型与异常处理Flow 响应式编程Compose 动画与副作用依赖注入测试,以及一个从零开始的完整项目实战

我想给你一个最后的忠告。在物理学里有一种东西叫做"cargo cult science" —— 表面上做了科学的所有形式(穿白大褂、用仪器、写论文),但缺少了核心:对真理的诚实追求。学编程也会有类似的陷阱:你可能完成了教程的所有章节、做了所有练习、甚至拿到了证书,但如果你只是在模仿而没有真正理解 —— 那就是"cargo cult programming"。

怎么避免?很简单:对自己诚实。每学完一个知识点,问自己:"我能不能用自己的话解释它?我能不能不看教程从零写出来?"如果不能,那就回去再看。这不丢人 —— 承认自己不懂是通往真正理解的唯一道路。

你会遇到看不懂的错误信息、调不通的 API、改了半天也改不好的 bug。这些不是失败 —— 这些是大自然在告诉你,你的假设在某个地方是错的。找到那个错误的假设,你就学到了比任何教程都更深刻的知识。

继续前进

最好的学习方式就是动手写代码,然后把你学到的教给别人。建议现在就继续阅读后面的章节,同时打开 Android Studio,把学到的知识在真实项目中用起来。不需要完美,不需要复杂 —— 从一个简单的想法开始,一步一步地把它变成一个能用的应用。记住,你不是在学编程 —— 你是在学习如何思考。而思考,是这个世界上最有趣的事情。

本章练习

练习 1:审视你自己的代码 入门

打开你之前写过的任何 Kotlin/Android 项目(或者本教程前几章的练习代码),全局搜索 !! 的使用。统计一下总共有多少处,然后对每一处问自己:这里为什么用 !!?是因为我确实验证过不会为 null,还是只是为了让编译器不报错?尝试用 ?.?:let 重写每一处 !!

提示

在 Android Studio 中使用 Cmd+Shift+F(macOS)或 Ctrl+Shift+F(Windows)全局搜索 !!。对于每一处,思考:如果这个值真的为 null 了会怎样?应用应该崩溃吗,还是应该有一个合理的默认行为?

参考答案

这是一个开放性练习,没有唯一的答案。关键在于养成习惯:每次想写 !! 的时候,先停下来问自己"如果这里真的是 null 怎么办?"。常见的替代方案包括:用 ?: 提供默认值(如 name?.length ?: 0);用 ?.let { } 在非空时执行操作;在函数入口处用 requireNotNull()checkNotNull() 做前置校验并给出清晰的错误信息。如果你的项目中 !! 的数量从两位数降到了个位数,说明你在向诚实靠近。

练习 2:费曼测试 —— 向初学者解释一个概念 进阶

从前面的章节中选择一个你觉得自己"已经理解了"的概念(比如:Kotlin 的空安全系统、Compose 的重组机制、协程的 suspend 函数、ViewModel 的作用),然后假装你面前坐着一个完全不懂编程的朋友。用纯文字(不写代码)向他解释这个概念,写至少 200 字。写完之后检查:有没有哪个地方你绕来绕去说不清楚?那个地方就是你还没有真正理解的地方。

提示

几个关键原则:(1)不要用任何英文术语,或者如果用了就必须先解释它;(2)多用类比 —— 比如把 null 比作"空信封",把协程比作"可以暂停的任务清单";(3)如果你发现自己在说"就是那个东西"或者"你知道的",那说明你在那个点上没有真正理解。写完之后大声读一遍 —— 一个不懂编程的人听完能明白吗?

参考答案

以"空安全"为例,一个好的费曼式解释可能是这样的:想象你是一个快递员,每天要送很多包裹。有些包裹箱里面有东西,有些是空的。普通的编程语言就像一个粗心的快递员 —— 他不检查箱子就直接拆开取东西,如果箱子是空的,他就会愣住不动了(程序崩溃)。Kotlin 的做法是在每个箱子上贴标签:有的标"一定有东西"(不可空类型),有的标"可能是空的"(可空类型)。编译器就像一个严格的主管,它会检查快递员的工作流程:如果你要打开一个"可能是空的"箱子,你必须先写好"如果是空的怎么办"的方案。这样就从根本上避免了"愣住不动"的情况。你在写解释的时候如果发现自己卡壳了 —— 比如你无法解释"为什么 String 和 String? 是不同的类型" —— 那就回到第 5 章重新学习那个部分。

练习 3:设计你的第一个项目计划 进阶

参考 10.4 节的路线图,为自己设计一个小型 Android 项目计划。要求:(1)用一句话描述你想做什么应用;(2)列出 3-5 个核心功能;(3)对每个功能,标注需要用到本教程哪一章的知识;(4)给自己设定一个合理的时间表;(5)为每个阶段写一个具体的"检验标准" —— 不是"学完 xxx",而是"能独立做到 xxx"。

提示

从简单开始。一个好的第一个项目应该:(1)功能足够少,一个人能在 2-3 周内完成;(2)覆盖"UI + 数据存储 + 基本交互"这三个核心要素;(3)是你自己真的想用的东西 —— 个人兴趣是最好的动力。检验标准的关键是"可验证" —— 不要写"理解 Room",要写"能独立创建一个 Room 数据库,定义 Entity 和 Dao,实现增删改查,不看教程"。

参考答案

以"个人阅读记录应用"为例:
一句话描述:一个记录我读过的书、想读的书、以及读书笔记的应用。
核心功能与对应章节:(1)书籍列表展示与搜索 —— 第 8 章 LazyColumn、状态管理;(2)添加/编辑书籍信息 —— 第 8 章 表单输入、第 9 章 ViewModel;(3)本地数据持久化 —— 第 9 章 Room 数据库;(4)阅读状态分类(想读/在读/已读) —— 第 5 章 枚举/密封类;(5)简单的统计页面(读了多少本等) —— 第 8 章 Compose 布局。
时间表:第 1 周:搭建项目骨架 + 书籍列表 UI;第 2 周:Room 数据库 + 增删改查;第 3 周:分类筛选 + 统计页面 + 打磨。
检验标准:第 1 周结束时,能用假数据显示一个可滚动的书籍列表;第 2 周结束时,添加的书籍能在应用重启后保留;第 3 周结束时,能向朋友演示完整功能,并解释为什么这样设计架构。

« 上一章:数据与网络 目录 下一章:泛型与异常处理 »