Android编程入门精要:从旧手机到最小工程实战
简介《Android编程入门精要》是一本面向零基础移动开发爱好者的实战型电子书以Java语言与Android平台为主线帮助读者从环境搭建一路走到完整App的构建。内容覆盖Java核心语法、Android Studio使用、UI界面设计、字符串资源与命名规范并重点展开ListView、Fragment等高级组件的实战应用以及数据存储与数据库操作等持久化技术适合作为Android开发工程师的入门起点。资源包内共1个PDF文件压缩包约17.5MB单文件结构便于在电脑或移动设备上直接阅读无需额外解压步骤。目前已有57人学习下载。全书以项目驱动方式组织读者可跟随案例理解列表展示、界面复用与数据管理的实现思路逐步掌握从界面到数据层的完整开发链路为后续进阶学习打下扎实基础。1. Android编程入门精要从一部旧手机到能跑通的最小工程很多人学 Android 的起点是买一本厚书终点是书架上多了一本没翻完的厚书。我带过几个刚转移动端的同事发现真正卡住他们的从来不是 Java 或 Kotlin 语法而是「工程跑不起来」——SDK 路径报红、Gradle 同步转圈、模拟器黑屏、真机连不上。Android编程入门精要这件事说白了就是把这条链路先打通一次让一个最小页面在真实设备上亮起来再去补语言和框架。它适合两类人有后端或前端基础、想扩到移动端的开发者以及完全零基础但愿意照着敲命令的新手。这一章不讲历史也不讲生态只讲清楚一件事入门阶段你真正要跨过的门槛是什么以及为什么我建议你从一部旧手机而不是模拟器开始。Android 的入门门槛和五年前不一样了。以前装个 Eclipse 加 ADT 就能写现在 Android Studio 一套下来动辄几个 GGradle 插件版本、JDK 版本、AGP 版本三者互相咬合错一个就同步失败。新手最容易在这里产生「我是不是不适合」的错觉其实只是版本矩阵没对上。我的建议是第一周不要碰 Compose、不要碰 Hilt、不要碰多模块就用一个 Activity 加一个 TextView把「改代码 → 编译 → 装到设备 → 看到变化」这个循环跑顺。这个循环一旦顺了后面学什么都是往这个骨架上挂东西。另外说一个反直觉的结论模拟器在入门阶段是负资产。它吃内存、启动慢、传感器行为诡异还容易让你误以为「代码有问题」其实是模拟器抽风。一部闲置的安卓手机打开开发者模式插上数据线比任何模拟器都诚实。下面几章我会按「环境 → 最小工程 → 界面与数据 → 避坑 → 进阶验证」的顺序把每一步的命令、参数和失败时的排查方向都写清楚。2. 环境搭建JDK、SDK 与 Gradle 的版本咬合怎么调环境这一步翻车的人最多而且翻车现场往往没有明确报错只有一句Gradle sync failed。要理解为什么会失败得先知道 Android 构建链路上有四个角色JDK 负责跑 Gradle 本身Gradle 负责调度构建Android Gradle PluginAGP负责把 Android 的构建逻辑插进 GradleSDK 提供编译和运行需要的平台文件。这四个角色各有版本且互相有兼容区间。你不需要背下整张兼容表但要知道「谁约束谁」。2.1 四个角色的版本约束关系约束关系是这样的AGP 版本决定它支持的 Gradle 版本区间Gradle 版本决定它支持的 JDK 版本区间而 compileSdk 决定你需要下载哪个平台的 SDK。所以正确的锁定顺序是「先定 AGP再定 Gradle再定 JDK最后定 compileSdk」。很多人反过来先装了最新 JDK结果 Gradle 版本太老不认报Unsupported class file major version然后开始到处搜越搜越乱。我一般会这样起步选一个稳定的 AGP 大版本比如 8.x 系列里的某个小版本然后查它要求的最低 Gradle装那个 Gradle 的对应版本JDK 用 17这是目前多数 AGP 8.x 的舒适区。compileSdk 跟着 AGP 建议走不要盲目追最新。这套组合定下来之后写进项目里团队里所有人用同一套能省掉大量「在我机器上是好的」的扯皮。2.2 用命令行验证环境是否真的通了图形界面会骗你命令行不会。装完 Android Studio 之后先别急着建项目打开终端把下面几条跑一遍。这几条命令的作用是确认 JDK、SDK 路径和平台工具都在位。# 确认 JDK 版本AGP 8.x 一般要求 17 java -version # 确认 ANDROID_HOME 指向 SDK 根目录没配的话手动 export echo $ANDROID_HOME # 列出已安装的 SDK 平台看有没有你要的 compileSdk sdkmanager --list_installed # 确认 adb 可用这是后面连真机的命脉 adb version逻辑说明java -version看的是当前 shell 用的 JDK不是 Android Studio 内置的那个两者可能不一致这是很多「IDE 里能跑、命令行不能跑」的根源。ANDROID_HOME没配的话Gradle 找不到 SDK会报SDK location not found。sdkmanager --list_installed能让你确认平台文件是否真的下载完整有时候下载中断会留下半个包编译时才报错。adb version是后面所有真机操作的前提。参数说明如果你机器上有多个 JDK用JAVA_HOME显式指定而不是靠 PATH 里的默认值。sdkmanager在较新的 SDK 里位于cmdline-tools/latest/bin下如果提示找不到命令先确认这个目录在 PATH 里。adb位于platform-tools下同样要进 PATH。2.3 建一个最小工程并锁定版本建工程时选「Empty Views Activity」而不是 Compose 模板减少变量。建完之后立刻打开两个文件项目根目录的settings.gradle和模块目录的build.gradle。把版本显式写死不要用动态版本号。// 模块级 build.gradle 里的关键片段 android { namespace com.example.minimal // 包名别用默认的 com.example compileSdk 34 // 与已安装 SDK 平台一致 defaultConfig { applicationId com.example.minimal minSdk 24 // 覆盖绝大多数在用设备 targetSdk 34 // 一般与 compileSdk 对齐 versionCode 1 versionName 1.0 } }逻辑说明namespace和applicationId在新版 AGP 里是分开的概念前者用于代码里的 R 类归属后者是安装到设备上的唯一标识。新手常把两者混为一谈改包名时只改一个结果装了两个图标。minSdk决定你的 App 能装在多老的设备上设太低会失去一些新 API设太高会丢掉用户24 是个常见折中。targetSdk影响系统对你的行为约束升级它往往意味着要处理新的权限和后台限制入门阶段先跟 compileSdk 对齐即可。参数说明compileSdk只影响编译期能看到哪些 API不影响运行minSdk影响运行期能否安装targetSdk影响运行期的行为兼容。这三个别搞混是后面排查「为什么新系统上行为不一样」的关键。3. 最小可运行工程一个 Activity 加一个按钮的完整链路环境通了之后第二步是让一个真实交互跑起来。我选「一个 Activity 加一个按钮点击后改变文字」这个例子因为它覆盖了 Android 入门最核心的四件事布局文件怎么写、Activity 怎么加载布局、控件怎么在代码里拿到、事件怎么绑定。这四件事通了你就能自己拼出大部分简单页面。3.1 布局文件与控件 id 的对应关系先在res/layout下写一个布局。布局是 XML描述界面的静态结构。每个需要被代码操作的控件都要有一个 id。!-- res/layout/activity_main.xml -- LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/android android:layout_widthmatch_parent android:layout_heightmatch_parent android:orientationvertical android:gravitycenter TextView android:idid/tv_status android:layout_widthwrap_content android:layout_heightwrap_content android:text初始状态 / Button android:idid/btn_change android:layout_widthwrap_content android:layout_heightwrap_content android:text点我 / /LinearLayout逻辑说明id/tv_status里的表示「如果没有这个 id 就新建一个」这是新手容易忽略的细节去掉在引用已有 id 时才用。layout_width和layout_height是必填项漏写直接编译报错。orientation决定子 View 的排列方向gravity决定内容在容器里的对齐方式这两个属性名字像但作用完全不同前者管子元素后者管自己内部。参数说明match_parent表示撑满父容器wrap_content表示按内容大小自适应。这两个值在入门阶段用得最多理解它们能解释大部分「为什么控件位置不对」的问题。3.2 Activity 里加载布局并绑定事件布局写好后在 Activity 的onCreate里加载它然后通过findViewById拿到控件引用绑定点击事件。// MainActivity.kt class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 加载布局 val tvStatus findViewByIdTextView(R.id.tv_status) val btnChange findViewByIdButton(R.id.btn_change) btnChange.setOnClickListener { // 点击后更新文字注意这里改的是 UI 线程上的 View tvStatus.text 已点击 ${System.currentTimeMillis()} } } }逻辑说明setContentView必须在findViewById之前调用否则布局还没加载找不到控件返回 null后面一用就崩。findViewById的泛型参数决定返回类型写错类型会在运行时抛ClassCastException。点击回调默认运行在主线程所以可以直接改 View这也是为什么不能在回调里做耗时操作——会卡界面。参数说明R.id.tv_status是编译期生成的资源引用如果布局里 id 拼错这里会直接编译不过这是好事比运行时崩强。System.currentTimeMillis()只是用来产生一个变化的字符串方便你确认点击真的生效了。3.3 装到真机并观察日志代码写完用数据线连上手机确认adb devices能看到设备然后执行安装。# 确认设备已授权连接 adb devices # 编译并安装 debug 包到已连接设备 ./gradlew installDebug # 实时看日志过滤本应用输出 adb logcat --pid$(adb shell pidof -s com.example.minimal)逻辑说明adb devices如果显示unauthorized说明手机上还没点「允许调试」解锁手机点一下即可。installDebug会先编译再安装比在 IDE 里点运行更能暴露构建问题。logcat按 pid 过滤能让你在满屏系统日志里只看自己的输出这是排查崩溃的第一手段。参数说明--pid后面跟进程号pidof -s取单个进程号。如果 App 还没启动pidof 会返回空先手动点开 App 再执行这条。崩溃日志里重点看Caused by那一行它通常指向真正的错误位置而不是最上面的FATAL EXCEPTION。4. 避坑与排查新手最常撞的五个墙这一章是我带人时记录下来的高频问题每条都按「现象 → 原因 → 解决」写。这些问题在搜索引擎里能搜到零散答案但很少有人把它们串成一条排查路径。你按顺序对照能省下大量试错时间。4.1 现象Gradle sync 一直转圈最后失败原因最常见的是网络拉不到依赖其次是 JDK 版本和 Gradle 不匹配。前者表现为卡在Downloading后者表现为Unsupported class file major version。还有一种隐蔽情况是本地 Gradle 缓存损坏表现为之前能同步的项目突然不行了。解决先看报错最后几行区分是网络还是版本。网络问题就配置国内镜像仓库在settings.gradle里把仓库地址换成可访问的镜像。版本问题就按第 2 章的锁定顺序重新对齐。缓存问题执行./gradlew --stop停掉守护进程再删掉用户目录下的.gradle/caches重试。注意删缓存会触发全量重新下载别频繁做。4.2 现象App 装上后点图标闪退原因九成是onCreate里抛了异常最常见的是findViewById返回 null 后直接调用方法或者布局文件里有不支持的属性。还有一种是把耗时操作放在了主线程触发了NetworkOnMainThreadException。解决立刻看 logcat找Caused by。如果是 null检查setContentView是否在findViewById之前以及 id 是否拼写一致。如果是主线程网络把网络请求挪到子线程或协程里。养成习惯每次改完代码先看 logcat 再下结论不要靠猜。4.3 现象真机连上了但 installDebug 提示设备离线原因数据线接触不良、USB 调试授权过期、或者手机上装了会抢占 adb 连接的安全类应用。还有一种情况是同时开了模拟器adb 在多个设备间分不清目标。解决先adb kill-server再adb start-server重启 adb 服务然后重新插拔数据线手机上重新确认授权弹窗。如果有多个设备用adb -s 设备序列号 install指定目标。序列号从adb devices里拿。这个问题的玄学成分在于线材换一根质量好的数据线往往比折腾软件有效。4.4 现象改了布局但运行后界面没变化原因改错了文件或者改的是layout但代码里加载的是另一个布局。还有一种情况是增量编译没生效装上去的还是旧包。解决先确认setContentView里的布局名和你改的文件名一致。然后执行一次 clean 再装./gradlew clean installDebug。如果还不行检查是不是有多个模块你改的模块不是当前运行的那个。多模块项目里这个坑很常见运行配置里选错模块改半天没反应。4.5 现象中文显示成方块或乱码原因源码文件编码不是 UTF-8或者字符串直接硬编码在代码里而没放进strings.xml。前者在跨平台协作时高发后者在需要多语言时是隐患。解决把 Android Studio 的全局编码和项目编码都设为 UTF-8。所有面向用户的文字都放进res/values/strings.xml代码里用getString(R.string.xxx)引用。这样既避免编码问题也为以后做多语言留了口子。硬编码字符串在入门阶段看着省事后期改起来是灾难。5. 进阶验证用一个小工具检验你是否真的入门了学到这一步判断自己有没有入门不看你看过多少教程看你能不能独立做出一件小事。我常用的检验题目是做一个「点击按钮记录次数退出再进来次数还在」的页面。这个题目小但覆盖了界面、事件、数据持久化三块而且能暴露你对生命周期和数据存储的理解深度。5.1 用 SharedPreferences 做最小持久化持久化有很多方案入门阶段先用 SharedPreferences它简单、同步、适合存少量键值对。下面这段代码把计数存进去启动时读出来。// 在 MainActivity 里读写计数 private val prefs by lazy { getSharedPreferences(demo_prefs, Context.MODE_PRIVATE) } private fun loadCount(): Int prefs.getInt(count, 0) private fun saveCount(value: Int) { // apply 是异步写commit 是同步写UI 场景用 apply prefs.edit().putInt(count, value).apply() }逻辑说明getSharedPreferences的第一个参数是文件名第二个是访问模式MODE_PRIVATE表示只有本应用能读。getInt的第二个参数是默认值键不存在时返回它这样第一次启动不会崩。apply把写操作放到后台不阻塞 UI但要注意它在进程被杀时可能来不及落盘对可靠性要求高的场景用commit。参数说明文件名不要用默认的显式命名便于以后排查。键名用常量管理避免拼写错误。存的数据类型要和读的一致存 int 读 string 会抛异常。5.2 生命周期与状态保存的边界这里有个容易翻车的点onCreate只在 Activity 创建时调一次但屏幕旋转、系统回收后台进程都会导致 Activity 重建。如果你只在onCreate里读数据旋转屏幕后计数可能回到旧值。正确的做法是在onResume或每次操作后立即保存而不是攒着最后一起写。我一般会这样处理点击时先更新内存里的值立刻saveCount再刷新界面。这样无论 Activity 什么时候重建读出来的都是最新的。这个习惯看起来啰嗦但能避免大量「数据莫名丢失」的问题。SharedPreferences 的读写本身很快不用太担心性能。5.3 一个可复现的验证清单做完上面的功能后按这个清单逐条验证全过才算真的跑通验证项操作预期结果首次启动安装后打开计数显示 0点击累加连点 5 次显示 5退出重进完全杀掉进程再打开仍显示 5旋转屏幕横竖屏切换计数不丢清数据系统设置里清除应用数据回到 0这张表的价值在于它把「我以为我会了」变成「我验证过了」。很多人卡在第三步退出重进数据没了原因就是只更新了界面没落盘。也有人卡在第四步旋转后回到 0原因是只在onCreate读了一次。这两条是入门阶段最典型的两个坑跨过去你对 Android 的状态管理就有了实感。最后说个我自己的习惯每学一个新组件我都会先写一个最小可运行版本跑通之后再往上加功能而不是一开始就搭一个「完整架构」。Android 的坑大多藏在版本和生命周期的细节里最小工程能让你快速定位问题大工程只会让问题互相掩盖。入门阶段慢就是快把这条链路走扎实后面学 Compose、学协程、学架构组件都只是在这个骨架上加东西。希望帮到你。本文还有配套的精品资源点击获取