Android 项目源码整理的最佳实现
这篇笔记整理自 CodePath Android Cliffnotes 里的 Organizing your Source Files, 再结合我自己的理解,聊聊一个 Android 项目的源码可以怎么收拾得干干净净。
主要围绕四件事:
- 命名要有自我介绍的能力
- 包结构要让人一眼看懂东西在哪儿
res目录别变成垃圾场- 布局文件太多时,用一点小工具帮你收纳
先把名字起好:Java 与 Android 类的命名约定
代码整理的第一步,其实就是把名字起对。名字对了,很多问题不用解释。
Java 基本命名
在 Java 代码里,可以坚持这几条简单规则:
变量、方法、参数:用驼峰命名(camelCase)
int incomeTaxRate;
int daysInMonth;
void convertToEuroDollars() { ... }
void depositAmount(double amount) { ... }
常量:全部大写,用下划线分隔单词
public static final int DAYS_IN_WEEK = 7;
这些规则本身并不新鲜,但统一才是关键:团队里所有人都按这套来, 哪怕是第一次看你代码的人,也能靠直觉分辨“这是变量、这是常量、这是方法”。
Android 特有类的命名
Android 里有一批“角色”非常固定:Activity、Fragment、Adapter、Service、数据库辅助类、网络层客户端等等。
文章建议是让类名把自己的“角色”写清楚。
例如:
CreateTodoItemActivity:一看就是一个界面。TodoItemsAdapter:配合列表用的适配器。TodoItemsDbHelper:操作 ToDo 数据库的 helper。TodoClient:封装网络请求的客户端。TodoItemFragment:某个局部 UI 的碎片。FetchTodoService:在后台拉取 ToDo 数据的服务。
规范很简单:把业务名 + 角色名拼在一起。
好处有两个:
- 在 IDE 左侧文件树里扫一眼,就能大致脑补出“这几个类在界面里是怎么配合的”。
- 搜索时非常方便,比如查
*Activity就能获得所有页面入口。
其他类型的类(比如纯工具、业务对象),可以按自己团队的习惯来,核心思路还是那句:看到名字就应该猜到它是干嘛的、属于哪一层。
怎样划分包
类名起好了,下一步就是把它们装进合适的抽屉,也就是包结构。
原文给了两种主流方案:按类别分,或按功能分。两种都常见,各有适用场景。
按类别分包:结构简单,适合体量不大的项目
这种方式,就是把“同一类角色”的东西放在一起:
com.example.myapp.activities:放所有Activitycom.example.myapp.fragments:放所有Fragmentcom.example.myapp.adapters:放所有自定义Adaptercom.example.myapp.models:放所有数据模型com.example.myapp.network:网络访问相关com.example.myapp.utils:工具方法、辅助类com.example.myapp.interfaces:接口定义
优点很直观:第一次进项目的人,能很快找到想看的那类文件。想看所有页面,就打开 activities 包;想看所有网络调用,就打开 network 包。
缺点也很明显:当项目长大之后,一个业务页面可能要同时修改 Activity、Fragment、Adapter、Model,这些文件散落在不同的包下,来回跳转比较频繁。
所以我更愿意把这种结构用在:Demo、小工具、教学项目,或者刚起步的产品。这时候代码量不大,“按类别收纳”足够了。
按功能(Feature)分包:项目一旦变复杂,就该上场
另一种做法是:以业务功能为中心组织包结构。
大致思路是:
com.example.myapp.service:后台任务、同步服务等com.example.myapp.ui:所有和界面相关的东西com.example.myapp.ui.mainscreen:主列表页面相关类com.example.myapp.ui.detailscreen:详情页面相关类- 以及其他功能模块,如
loginscreen、settings等
这样,一个功能模块需要的 Activity、Fragment、Adapter、ViewModel(如果有)都聚在一起。
优点:
- 开发时心智负担小:我要改详情页,只要打开
detailscreen包。 - 更适合多人协作:一个人负责一个功能包,冲突会少很多。
- 后期做“按模块拆库”时,也更容易抽取出完整的模块。
从 Java 开发的经验来看,按功能打包通常比按类别更适合中大型项目,原文也明确给了这样的建议。
一个折中做法是:项目刚开始按类别分包,等功能逐渐稳定、体量上来以后,慢慢往按功能分包演进,而不是一开始就大动干戈。
别把 res 目录搞成垃圾堆
写 Android 的人都知道,真正会爆炸的往往不是 Java 代码,而是 res 目录,
一堆 layout、drawable、values 混在一起,一两年之后,谁也不敢随便删。
先回顾一下最常用的几个资源目录,以及它们各自负责什么:
| 名称 | 路径 | 用途 |
|---|---|---|
| 布局 | res/layout/ |
XML 布局文件 |
| 菜单 | res/menu/ |
AppBar 等菜单定义 |
| 图像/形状 | res/drawable/ |
PNG、矢量图、shape 等 |
| 颜色 | res/values/colors.xml |
颜色定义 |
| 尺寸 | res/values/dimens.xml |
间距、字号等尺寸 |
| 字符串 | res/values/strings.xml |
所有文案 |
| 样式 | res/values/styles.xml |
主题、样式 |
真正影响项目可维护性的,是两件小事:
不在布局里硬编码颜色和尺寸
例如,下面这种写法看上去省事,但后期会非常难维护:
<TextView
android:text="Error"
android:textColor="#ff0000"
android:paddingLeft="8dp" />
改成下面这样,对自己和队友都友好得多:
<TextView
android:text="@string/error_message"
android:textColor="@color/error_red"
android:paddingLeft="@dimen/item_padding_left" />
做法简单:
- 所有颜色统一放到
colors.xml,用语义化命名,比如error_red、primary、divider。 - 所有尺寸统一放到
dimens.xml,按用途命名,比如list_item_padding、toolbar_height。 - 文案全部进
strings.xml,哪怕现在还没有多语言需求,未来想加时也不会太痛苦。
用好 Android 的资源限定符
除了颜色和尺寸,Android 还提供了非常丰富的“配置限定符”:可以针对横竖屏、平板/手机、不同屏幕密度做不同的布局和图片资源,比如 layout-land、drawable-hdpi 等。
原文提醒我们,在代码里少写条件判断,多利用资源系统本身的能力。
比如,不要在 Java 里写一堆 if (isTablet()) 去切换布局,而是准备两套 layout 文件,交给系统根据配置自动选择。
布局文件太多怎么办:用虚拟子目录“收纳”
随着界面增多,res/layout 下面可能会躺着上百个 XML 文件。
默认情况下,Android Studio 只会给你一个长长的扁平列表——找文件就像在回收站里翻东西。
一个比较实用的小技巧,是利用 Android Studio 的第三方插件,把布局文件“虚拟分组”。 原文推荐的是一款 folding 插件,可以在 IDE 里创建“虚拟文件夹”。
大概效果是这样的:
-
原来
layout目录里是一长串:chat_activity.xml chat_item_image.xml chat_item_text.xml home_fragment.xml home_toolbar.xml ... -
装上插件之后,可以在布局文件上右键,选择类似 Group 的菜单项,把文件按前缀或功能归到一起,变成:
layout ├── chat │ ├── chat_activity.xml │ ├── chat_item_image.xml │ └── chat_item_text.xml ├── home │ ├── home_fragment.xml │ └── home_toolbar.xml └── ...
需要特别说明两点(这也是原文反复强调的):
这些“子目录”只是 IDE 里的分组,不会真的在磁盘上创建新文件夹。
插件也不会帮你移动任何文件,它只是改变显示方式。
所以它非常安全:只要不顺眼,关掉插件一切恢复原样。
在一个旧项目里渐进式整理布局时,这个小工具会带来非常明显的心情改善。