☰
Android 开发最佳实践指南:基于 android-best-practices 仓库的 Futurice 工程规范与实战要点
2026/10/1 2:36:35 网站建设 项目流程
  • 文档
  • 教程
  • 移动开发

【免费下载链接】android-best-practices

Do's and Don'ts for Android development, by Futurice developers

项目地址:https://gitcode.com/gh_mirrors/an/android-best-practices
点击查看免费下载

本篇技术指南以本仓库中的 土耳其语翻译版最佳实践文档 为骨架,系统梳理 Futurice 团队在 Android 开发中沉淀的工程规范:从 Gradle 构建配置、项目结构、第三方库选型,到资源文件组织、测试框架、ProGuard 与数据存储,覆盖 Android 应用从搭建到发布的完整链路。读者读完可掌握一套可直接落地的 Android 工程实践清单,并能依据仓库根目录下的 英文原版 README.md 对照出这些规范随生态演进的脉络,为自己的项目做出更贴合现状的技术决策。


一、Android SDK:放对位置,省去重装之苦

将 Android SDK 放置在与应用程序无关、方便访问的位置(例如用户主目录)。部分 IDE 在安装时会自带 SDK,并把它放到 IDE 所在的目录之下,这并不方便:当你需要升级(或重装)IDE 时,可能连 SDK 一起丢失,被迫进行漫长而枯燥的重新下载。

另外,如果你的 IDE 不是以 root 权限运行的,还应避免把 SDK 放在需要系统级权限(sudo/admin)的目录中,以免引发权限问题。这一原则在 README.md 中同样被强调——SDK 位置应该独立于 IDE,成为开发者自己可控的资源。

二、构建系统:让 Gradle 成为默认选项

构建系统应默认选择Gradle(配合 Android Gradle 插件)。相比 Ant——功能更受限、需要更多命令——Gradle 具备以下能力:

  • 构建应用的不同变体(build variants)
  • 编写可读的自定义任务
  • 管理与下载依赖
  • 组织 keystore 签名配置
  • 以及更多功能

Android 的 Gradle 插件由 Google 持续开发,正逐步成为依赖管理方面最主流的方案。更重要的是,应用的构建流程应当由 Gradle 文件定义,而非依赖 IDE 专属配置,这样才能保证不同工具之间构建结果一致,并为持续集成系统提供良好支持(这一点在英文版 README.md 中有明确说明)。

三、项目结构:从旧结构迁移到新结构

历史上存在两种流行的项目结构:旧的 Ant & Eclipse ADT 结构,与新的 Gradle & Android Studio 结构。应选择新结构;若还在使用旧结构,应视其为历史遗留并尽快迁移。

旧结构:

old-structure ├─ assets ├─ libs ├─ res ├─ src │ └─ com/futurice/project ├─ AndroidManifest.xml ├─ build.gradle ├─ project.properties └─ proguard-rules.pro

新结构:

new-structure ├─ library-foobar ├─ app │ ├─ libs │ ├─ src │ │ ├─ androidTest │ │ │ └─ java │ │ │ └─ com/futurice/project │ │ └─ main │ │ ├─ java │ │ │ └─ com/futurice/project │ │ ├─ res │ │ └─ AndroidManifest.xml │ ├─ build.gradle │ └─ proguard-rules.pro ├─ build.gradle └─ settings.gradle

两种结构的核心差异在于:新结构明确用 Gradle 区分了不同"源集"(source sets),例如main与androidTest。你可以在src下增加paid、free等源集目录,让应用按需获得不同特性。保持一个高质量的app文件夹,能将应用与其它库项目清晰分离;settings.gradle则负责记录app/build.gradle将要引用的库。英文原版 README.md 也指出:除非有充分理由,否则应接受 Gradle 的默认结构,以简化构建脚本。

四、Gradle 配置要点

4.1 通用结构

总体结构请遵循 Google 官方的 Android Gradle 用户指南(见 README.md 中推荐的做法)。

4.2 小任务:用 Gradle 而不是外部脚本

对于构建过程中的小任务,与其编写 shell、Python、Perl 等外部脚本,不如直接用 Gradle 实现。可参考 Gradle 官方文档中的任务编写方式;英文原版还补充了 Google 提供的 Android 专属 Gradle 食谱(详见 README.md)。

4.3 密码与敏感数据:务必放进 gradle.properties

在build.gradle中需要为发布版本定义signingConfigs。不要这样写——明文密码会进入版本控制系统:

signingConfigs { release { storeFile file("myapp.keystore") storePassword "password123" keyAlias "thekey" keyPassword "password789" } }

正确做法是:创建一份不要提交到 VCS的gradle.properties文件:

KEYSTORE_PASSWORD=password123 KEY_PASSWORD=password789

Gradle 会自动导入该文件,因此在build.gradle中可以直接引用,并用try/catch兜底提示:

signingConfigs { release { try { storeFile file("myapp.keystore") storePassword KEYSTORE_PASSWORD keyAlias "thekey" keyPassword KEY_PASSWORD } catch (ex) { throw new InvalidUserDataException("You should define KEYSTORE_PASSWORD and KEY_PASSWORD in gradle.properties.") } } }

4.4 优先用 Maven 依赖解析,而不是直接导入 jar

直接往项目里塞 jar 文件,会使依赖冻结在某个特定版本(如2.1.1),下载更新与版本更换都很繁琐——这正是 Maven 已解决的问题。应尽量使用 Maven 坐标声明依赖,例如:

dependencies { implementation 'com.squareup.okhttp:okhttp:2.2.0' implementation 'com.squareup.okhttp:okhttp-urlconnection:2.2.0' }

4.5 避免动态依赖版本号

避免使用2.1.+这类动态版本号:不同构建之间可能引入未察觉的行为差异,产生难以排查的隐性 bug。使用2.1.1这样的静态版本,才能得到稳定、可预测、可复现的构建环境(英文原版 README.md 表述与此一致)。

4.6 非发布构建使用独立的包名后缀

利用applicationIdSuffix为debug构建类型添加后缀,使 debug 与 release 两个 APK 能同时安装在同一台设备上——应用发布后这一点尤其重要:

android { buildTypes { debug { applicationIdSuffix '.debug' versionNameSuffix '-DEBUG' } release { // ... } } }

同时建议为不同构建类型配置不同图标,方便在设备上区分。Gradle 使这变得非常简单:在默认项目结构下,将debug图标放入app/src/debug/res,release图标放入app/src/release/res;也可以通过versionName或按构建类型修改应用名称来实现。

五、IDE 与文本编辑器

你可以使用任何能正确遵循项目结构的文本编辑器,编辑器选择纯属个人偏好,但务必营造一个能遵循项目结构的工作环境。

当前最受推荐的 IDE 是Android Studio,理由包括:由 Google 开发维护、原生支持 Gradle、内置新项目结构模板、专为 Android 开发打造、且已趋于稳定。

而Eclipse ADT已不再被推荐使用:Google 已于 2015 年底停止 ADT 支持,并建议用户迁移到 Android Studio。若仍继续使用 Eclipse,由于它基于旧项目结构与 Ant,你需要额外配置或从命令行运行 Gradle。

同样可以使用 Vim、Sublime Text、Emacs 等文本编辑器,但此时需要习惯在命令行中运行 Gradle 与 adb。

无论选择何种编辑器,都要确保 Gradle 与项目结构符合规范,且不要将编辑器专属文件提交到 VCS(例如 Ant 的build.xml)。务必保持build.gradle最新且可运行,时刻替其他开发者着想——他们不应被迫为你收拾项目环境。英文原版 README.md 还补充:应避免将 Android Studio 的.iml等本地配置提交到版本控制,它们往往包含你本机的专属配置,对同事无效。

六、第三方库选型

6.1 JSON 解析:Jackson

Jackson是一个 Java 的 JSON 序列化/反序列化库。同类中最常用的还有Gson,但 Jackson 之所以被优先推荐,是因为它提供多种 JSON 处理方式:流式解析(streaming)、内存树模型(in-memory tree model),以及经典的 JSON-POJO 数据绑定(data binding)。

需要留意的是,Jackson 体积比 Gson 更大,在 65k 方法数限制下可能需要对库做取舍调整。其他可选方案还有 Json-smart、Boon JSON。英文原版 README.md 则指出:Gson 体积更小,若想规避 65k 方法限制可以优先考虑;Square 的 Moshi 则吸收了 Gson 的开发经验并与 Kotlin 集成良好。

6.2 网络、缓存与图片:不要重复造 HTTP 客户端

向后端服务器发起请求,已有大量久经实战检验的客户端方案,不应再自己实现 HTTP 客户端。土耳其语版本推荐的是Volley或Retrofit:

  • Volley:同时提供图片加载与缓存能力;
  • Retrofit:选择它时,可搭配Picasso做图片加载、OkHttp做高效的 HTTP 请求。三者同属一家公司(Square),相互配合良好;OkHttp 也可以与 Volley 结合使用。

英文原版 README.md 的建议在此基础上演化为:以OkHttp为基础提供高效 HTTP 请求,用Retrofit提供类型安全层,图片加载缓存考虑Picasso;Glide是另一个图片加载选项,支持 GIF 动图、圆形图片,但方法数也更大。

6.3 响应式编程:RxJava

RxJava是用于响应式编程(Reactive Programming)的库,本质是处理异步事件。这是一个强大但也陡峭的范式,在用它重构整个应用架构之前应审慎评估。土耳其语版本还给出了循序渐进的学习路径:

  • 没有 Rx 经验时,先仅将其应用于后端 API 响应的处理;
  • 或者先用于简单的 UI 事件处理(如点击事件、搜索框输入监听);
  • 当确信技能足够、想将其推广到整个架构时,务必为所有棘手之处编写 Javadoc——因为未来接手项目的同事可能对 Rx 一无所知,代码应尽可能易读。

英文原版 README.md 进一步建议配合RxAndroid(Android 线程支持)与RxBinding(将 Android 组件转化为 Observable),并以 Futurice 的开源应用 Freesound Android 作为大量使用 RxJava 2 的参考案例。

6.4 Lambda 语法:Retrolambda

Retrolambda允许在 Android 及 JDK-8 之前的平台上使用 Lambda 表达式语法,使代码更紧凑清晰,尤其在配合 RxJava 的函数式写法时获益明显。配置步骤如下:

  1. 安装 JDK 8;
  2. 设置JAVA8_HOME与JAVA7_HOME系统变量;
  3. 在build.gradle的根构建脚本中添加:
dependencies { classpath 'me.tatarka:gradle-retrolambda:2.4.1' }
  1. 在每一个 Gradle 模块中应用插件并配置:
apply plugin: 'retrolambda' android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } retrolambda { jdk System.getenv("JAVA8_HOME") oldJdk System.getenv("JAVA7_HOME") javaVersion JavaVersion.VERSION_1_7 }

Android Studio 本身也为 Java 8 Lambda 提供代码辅助。若你是 Lambda 新手,记住两点即可:

  • 只含一个方法的接口是"lambda 友好"的,可以折叠成更紧凑的语法;
  • 拿不准参数写法时,先写匿名内部类,再让 Android Studio 帮你自动折叠成 Lambda。

需要说明:英文原版 README.md 指出,从 Android Studio 3.0 起已不再需要 Retrolambda。

6.5 警惕 65k 方法数限制:克制使用库,尤其避开 Guava

Android 应用打包为 dex 文件后,可被引用的方法数存在65536 的硬上限;一旦超过,编译将直接报致命错误。因此应:

  • 尽量使用少量的库;
  • 用 dex-method-counts 这类工具计算哪些库组合能保持在限制之下;
  • 尤其避免 Guava——它内部包含超过 13k 个方法。

七、Activity 与 Fragment 的取舍

社区乃至 Futurice 内部对如何用 Activity 与 Fragment 组织最佳架构并无共识。Square 甚至推出了 Mortar 这类主要基于 View 构建架构的库来绕开 Fragment,但这在社区中仍未被广泛认可。

从 Android API 的发展历史看,可以粗略地把Fragment 视为屏幕的 UI 片段(通常与 UI 相关),把Activity 视为控制器(对生命周期与状态管理尤为重要)。不过角色也存在变化:Activity 可能承担 UI 角色(如屏幕切换过渡),Fragment 也可能被单独用作控制器。每种方案(纯 Fragment 架构、纯 Activity 架构、纯 View 架构)都有各自的缺点,应审慎决策。以下几点值得注意(也请结合自身场景判断):

  • 避免过度使用嵌套 Fragment:易触发"套娃 bug"(matryoshka bugs)。仅在确实合理的场景使用,例如屏幕型 Fragment 内、水平滑动的 ViewPager 中的子 Fragment;
  • 避免在 Activity 中堆积大量代码:尽可能让 Activity 保持轻量,主要承担生命周期与 Android 交互 API 的职责。优先使用"单 Fragment 的 Activity",把 UI 代码放进 Fragment——这为将来改为 Tab 布局或多 Fragment 平板界面留出复用空间。每个 Activity 尽量对应一个 Fragment,除非你已深思熟虑。

另外要谨慎对待操作系统层面的操作,最大的坑往往出现在 Intent 使用上:这些 Intent 会影响 Android 操作系统或其他应用,可能引发 bug。例如,若应用通过 Intent 进行跨包通信,设备开机后可能出现数秒延迟。

八、Java 包结构:遵循 MVC 思想组织代码

Java 层的 Android 架构大体可用Model-View-Controller(MVC)来描述:在 Android 中,Fragment 与 Activity 承担控制器角色(同时也是 UI 的一部分)。正因如此,把 Fragment 或 Activity 直接放进 view 或 controller 包里是不妥的,放入fragments包更合理。若遵循前述"Activity 尽量保持轻量"的指引,将 Activity 放在顶层也无妨;若需要 2、3 个及以上 Activity,可放入activities包。

其余部分则是典型的 MVC 形态:

  • models:存放从 API 响应 JSON 反序列化得到的 POJO;
  • views:存放自定义 View(通知、ActionBar 视图、widget 等);由于 Adapter 是数据与视图之间的中介、且常通过getView()获取视图,adapters可作views的子包;
  • managers:存放覆盖整个应用、贴近 Android 系统层的控制器类;
  • utils:存放各类数据处理类(如DateUtils);
  • network:存放与后端通信的类。

从最接近后端到最接近用户,整体结构如下:

com.futurice.project ├─ network ├─ models ├─ managers ├─ utils ├─ fragments └─ views ├─ adapters ├─ actionbar ├─ widgets └─ notifications

值得一提的是,英文原版 README.md 已将该建议演进为基于特性(feature-based)的包结构,其收益包括:更清晰的特性依赖与接口边界、促进封装、更易理解组成特性的组件、降低误改无关共享代码的风险、导航更简单、更易移除特性,以及更易过渡到模块化构建(更好的构建时间与 Instant Apps 支持)。

九、资源文件管理

9.1 命名规范

遵循type_foo_bar.xml的前缀约定,例如:fragment_contact_details.xml、view_primary_button.xml、activity_main.xml。

9.2 Layout XML 的组织方式

如果不确定如何编排 layout XML,可遵循以下约定:

  • 每行一个属性,缩进 4 个空格;
  • android:id永远放在第一个;
  • android:layout_****类属性放在前面;
  • style属性放在最后;
  • 标签闭合符/>单独占一行,便于后续添加或调整属性;
  • 与其硬编码android:text,不如使用 Android Studio 支持的 Design-time 属性(tools 命名空间)。

示例:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/name" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:text="@string/name" style="@style/FancyText" /> <include layout="@layout/reusable_part" /> </LinearLayout>

核心原则:android:layout_****(位置、大小、边距等)类属性应定义在 layout XML 中;其余android:****外观属性(颜色、内边距、字体等)应放进 style XML。该原则存在例外:

  • android:id显然应在 layout 文件中;
  • LinearLayout的android:orientation放在 layout 文件中通常更合理;
  • android:text定义内容,应放在 layout 文件中;
  • 有时可为一个通用 style 定义android:layout_width与android:layout_height,但默认情况下它们应出现在 layout 文件中。

9.3 使用 Style:避免重复属性

几乎每个项目都应当恰当地使用 style——同一个 view 的外观被反复复用的情形非常普遍。例如为正文文本定义一个通用 style:

<style name="ContentText"> <item name="android:textSize">@dimen/font_normal</item> <item name="android:textColor">@color/basic_black</item> </style>

应用到 TextView:

<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/price" style="@style/ContentText" />

按钮类同此理。但这只是起点——把一组相关且重复出现的android:****属性整体迁移到公共 style,收益更大。

9.4 拆分大型 style 文件

你不需要只有一个styles.xml。Android SDK 原生支持其他文件名——styles这个名字本身没有魔法,真正起作用的是文件内的<style>标签。因此完全可以拆分出styles.xml、styles_home.xml、styles_item_details.xml、styles_forms.xml等多个文件。与res目录名(构建系统能识别其含义)不同,res/values下的文件名可以是任意的。

9.5 colors.xml:只做调色板

colors.xml里只应存在"颜色名 → RGBA 值"的映射,不要用button_foreground、comment_background_active这类按用途命名的颜色。否则同一 RGBA 值会在多处重复,一次简单的颜色调整就要改动许多文件;而且这些用途说明本应属于 style,不该出现在colors.xml。

不要这样写:

<resources> <color name="button_foreground">#FFFFFF</color> <color name="button_background">#2A91BD</color> <color name="comment_background_inactive">#5F5F5F</color> <color name="comment_background_active">#939393</color> <color name="comment_foreground">#FFFFFF</color> <color name="comment_foreground_important">#FF9D2F</color> ... <color name="comment_shadow">#323232</color>

应该这样写:

<resources> <!-- grayscale --> <color name="white" >#FFFFFF</color> <color name="gray_light">#DBDBDB</color> <color name="gray" >#939393</color> <color name="gray_dark" >#5F5F5F</color> <color name="black" >#323232</color> <!-- basic colors --> <color name="green">#27D34D</color> <color name="blue">#2A91BD</color> <color name="orange">#FF9D2F</color> <color name="red">#FF432F</color> </resources>

这份调色板可以向应用的设计师索取。颜色名不必是"白色""蓝色"等字面颜色名,brand_primary、brand_secondary、brand_negative这类语义化命名同样完全可以接受。以这种方式组织颜色,改动更简单,也能对用色数量保持掌控——对美观的 UI 而言,控制颜色变体数量很重要。

英文原版 README.md 还补充了更彻底的三层分离模型:colors.xml只定义调色板;styles.xml引用调色板并反映颜色用途(如按钮前景为白色);activity_main.xml引用 style 为按钮着色。必要时还可以再加一层"用途颜色"资源文件,例如<color name="button_foreground">@color/white</color>,再由 style 引用之——这种方式利于颜色重构与样式稳定性,代价是需维护另一套颜色映射。

9.6 dimens.xml:像 colors.xml 一样组织

出于与颜色相同的目的,也应该为字体大小与边距间距定义一个"调色板"。一个良好的 dimens 文件示例:

<resources> <!-- font sizes --> <dimen name="font_larger">22sp</dimen> <dimen name="font_large">18sp</dimen> <dimen name="font_normal">15sp</dimen> <dimen name="font_small">12sp</dimen> <!-- typical spacing between two views --> <dimen name="spacing_huge">40dp</dimen> <dimen name="spacing_large">24dp</dimen> <dimen name="spacing_normal">14dp</dimen> <dimen name="spacing_small">10dp</dimen> <dimen name="spacing_tiny">4dp</dimen> <!-- typical sizes of views --> <dimen name="button_height_tall">60dp</dimen> <dimen name="button_height_normal">40dp</dimen> <dimen name="button_height_short">32dp</dimen> </resources>

在布局中,margin 与 padding 应使用spacing_****这类通用常量,而不是硬编码数值——就像对待普通字符串一样。这能带来统一的外观体验,也让样式与布局更易组织与修改。

9.7 strings.xml 的命名

字符串的 key 应像命名空间一样组织,不要害怕让两个或多个 key 复用同一个值——语言是复杂的,命名空间能为字符串补充上下文、消除歧义。

糟糕的命名:

<string name="network_error">Network error</string> <string name="call_failed">Call failed</string> <string name="map_failed">Map loading failed</string>

良好的命名:

<string name="error.message.network">Network error</string> <string name="error.message.call">Call failed</string> <string name="error.message.map">Map loading failed</string>

此外,不要把字符串值全部写成大写,遵循正常文本惯例(例如首字母大写)。若界面需要全大写展示,请使用 TextView 的textAllCaps属性:

糟糕:

<string name="error.message.call">CALL FAILED</string>

良好:

<string name="error.message.call">Call failed</string>

9.8 避免过深的 View 层级

有时为了完成某个布局效果,你会忍不住再嵌套一层 LinearLayout,结果可能演变成下面这样的结构:

<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <RelativeLayout ... > <LinearLayout ... > <LinearLayout ... > <LinearLayout ... > </LinearLayout> </LinearLayout> </LinearLayout> </RelativeLayout> </LinearLayout>

即使 layout 文件中看不到这种显式嵌套,也可能在 Java 代码中动态 inflate 视图时发生。其后果包括:处理复杂 UI 树带来的性能问题,以及更严重的StackOverflowError风险。

因此应尽量保持视图层级扁平:学习使用RelativeLayout(英文原版 README.md 已更新为推荐 ConstraintLayout)、掌握布局优化技巧,并研究<merge>标签的用法。

9.9 WebView:警惕客户端处理与内存泄漏

当必须展示网页(例如新闻文章页)时,应避免在客户端侧做 HTML 清理处理,而是要求后端程序员提供"纯净"的 HTML。WebView 若持有 Activity 引用而非绑定 ApplicationContext,会产生内存泄漏。另外,简单的按钮和表单不必用网页实现,优先使用原生控件。

十、测试框架

Android SDK 自带的测试框架,尤其是 UI 测试方面,当时仍处于起步阶段。Android Gradle 通过connectedAndroidTest提供设备测试支持,基于 Android 的 JUnit 扩展与辅助类运行,因此需要在真实设备或模拟器上执行测试。

10.1 单元测试:Robolectric

土耳其语版本推荐使用Robolectric进行单元测试——它能在"脱离设备"的情况下运行单元与 UI 测试。但要注意:在 Robolectric 上做 UI 测试并不准确,因为无法观察元素间的动画等行为,测试价值有限。

需要对照的是,英文原版 README.md 已明确不推荐 Robolectric:Android 构建系统对 JUnit 的支持改善后,Robolectric 通过提供 Android 平台的 mock 实现来"脱离设备"测试,无法保证正确性;更好的组合是"JVM 上的纯 JUnit 单元测试 + 设备上的集成测试"。

10.2 UI 测试:Robotium

Robotium让 UI 测试编写变得简单。虽然可以写互相独立的 UI 测试,但编写有依赖关系、串联操作的测试更能验证应用流程。示例代码极其直观:

solo.sendKey(Solo.MENU); solo.clickOnText("More"); // searches for the first occurrence of "More" and clicks on it solo.clickOnText("Preferences"); solo.clickOnText("Edit File Extensions"); Assert.assertTrue(solo.searchText("rtf"));

对照英文原版 README.md,如今的推荐已更新为Espresso编写 UI 测试、AssertJ-Android让断言更简洁,例如:

// Example assertion using AssertJ-Android assertThat(layout).isVisible() .isVertical() .hasChildCount(5);

十一、模拟器

若以 Android 开发为职业,土耳其语版本建议获取Genymotion模拟器的许可证:其帧率(frame/sec)比 AVD 模拟器更快,内置网络质量测试、GPS 定位模拟等工具,适合批量测试场景,可在一台机器上覆盖多设备、多 Android 版本,比购置多台实体机划算得多。

两点警告:Genymotion 不包含 Play Store 与 Maps 等 Play 服务;且若要测试三星专属 API,仍建议使用真机。

对照英文原版 README.md,官方模拟器(尤其 x86 变体)近年性能已显著提升,足以应对大多数日常开发场景;但真机验证仍不可忽视——重点应放在市场份额大、与你的应用最相关的设备上。

十二、ProGuard 配置

ProGuard通常用于 Android 项目的代码压缩(shrink)与混淆(obfuscate)。是否启用取决于项目配置,通常会在发布 APK 时让 Gradle 启用 ProGuard:

buildTypes { debug { minifyEnabled false } release { signingConfig signingConfigs.release minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }

为确定哪些代码需要保留、哪些可以丢弃或混淆,你必须为代码指定一个或多个入口点——通常是含 main 方法的类、applet、midlet、Activity 等。Android 框架的默认 ProGuard 配置位于SDK_HOME/tools/proguard/proguard-android.txt;使用上述配置时,项目专属规则(my-project/app/proguard-rules.pro)会被追加到默认配置之后。

12.1 常见错误与排查

assembleRelease等构建命令成功运行,但应用启动即崩溃、抛出ClassNotFoundException或NoSuchFieldException之类的错误,是 ProGuard 最典型的坑。它通常意味着两种可能:

  1. ProGuard 认为某类/枚举/方法/字段/注解"不再需要",将其删除;
  2. ProGuard 混淆(重命名)了类/枚举/字段名,但代码某处仍通过原名间接引用(如 Java 反射)。

排查方法:

  • 查看app/build/outputs/proguard/release/usage.txt,确认目标对象是否被移除;
  • 查看app/build/outputs/proguard/release/mapping.txt,确认目标对象是否被混淆。

12.2 keep 与 keepnames

阻止 ProGuard剥离必需类或成员,在配置中加入keep:

-keep class com.futurice.project.MyClass { *; }

阻止 ProGuard混淆类或成员,加入keepnames:

-keepnames class com.futurice.project.MyClass { *; }

更多示例见 ProGuard 官方手册。

12.3 尽早、反复地做 release 构建

在项目早期阶段就应生成并测试发布 APK,以确认 ProGuard 规则是否正确保留了依赖。每当引入新库或更新依赖时,同样构建一个发布 APK 并在设备上实测。不要等到应用临近 "1.0" 才首次构建 release——那时你可能面对一堆意外 bug,却几乎没有修复时间。

提示:为每个发布给用户的版本保留mapping.txt。这样当用户上报 bug、提交混淆后的堆栈时,你能更快定位问题。

12.4 DexGuard

若需要更强大的发布代码优化与混淆工具,可研究DexGuard——由打造 ProGuard 的同一团队推出的商业产品,它还能轻松拆分 dex 文件,从而解决 65k 方法数限制问题。

十三、数据存储

13.1 SharedPreferences:简单持久化的默认选择

如果应用运行在单一进程、且无需持久化复杂数据,SharedPreferences 就是合理的默认选项。以下情况则不适用:

  • 性能:数据复杂或数据量很大;
  • 多进程访问:多个进程需要访问并同步数据(例如拥有独立进程的 widget 或远程服务);
  • 关系型数据:数据各部分存在关系且需要强制维护这些关系。

英文原版 README.md 补充:也可将复杂对象序列化为 JSON 再存储、读取时反序列化,但要权衡这种方式的性能与可维护性。

13.2 ContentProviders:平台标准方案

当 SharedPreferences 不够用时,应使用平台标准的ContentProviders——更快且进程安全。其唯一缺点是搭建所需的样板代码较多(加上劣质教程的误导)。不过可以借助Schematic之类的库自动生成 ContentProvider,显著减少工作量。

仍需要自行编写从 SQLite 列读取数据对象(及反向)的解析代码。另一种思路是:用 Gson 等库将数据对象序列化后只持久化字符串——性能有所损失,但不必为数据类的每个字段声明一列。

13.3 ORM:除非确有需要,否则不推荐

通常不推荐使用 ORM(对象关系映射)库,除非数据异常复杂且有迫切需求——ORM 往往复杂且学习成本高。若决定使用 ORM,务必确认其是否process safe(进程安全),因为许多现有 ORM 方案恰恰不安全。

十四、使用 Stetho 调试

Stetho是 Facebook 开发的 Android 调试桥(debug bridge),与 Chrome 桌面浏览器的开发者工具集成。借助它可轻松检查应用状态,尤其是网络流量,还能查看与编辑应用内的 SharedPreferences 与 SQLite 数据库。务必确保 Stetho只在 debug 构建中启用,绝不要出现在 release 变体中。

英文原版 README.md 还补充了备选方案Chuck:功能更简化,但日志直接显示在设备上,对测试人员更友好(无需 Chrome 连接配置)。

十五、与仓库现状的演进对照

本文主体依据仓库中的 土耳其语翻译版 整理,它忠实记录了 Futurice 团队在某一时期(对应 Android Studio 初代、65k 方法限制盛行、Robolectric/Robotium/Genymotion 当道的年代)的实践总结。若对照仓库根目录的 英文原版 README.md,可清晰看到以下演进脉络,供读者决策时参考:

主题土耳其语版(本文主体)英文原版 README.md
构建Gradle + 自定义结构强调 Gradle 默认结构、建议minSdkVersion: 21(API 21 起不再需要 multidex 支持库)
网络Volley / OkHttpOkHttp + Retrofit + Picasso/Glide
JSONJackson(优先)Jackson 可选,Gson 更小、Moshi 集成 Kotlin
LambdaRetrolambdaAndroid Studio 3.0 起不再需要
包结构MVC 分层(network/models/managers/utils/fragments/views)基于特性的包结构(feature-based)
布局优化RelativeLayoutConstraintLayout
测试Robolectric + RobotiumJUnit + Espresso + AssertJ-Android
模拟器Genymotion官方模拟器已够用,辅以真机
调试StethoStetho + Chuck
新增主题——LeakCanary 内存泄漏检测、持续集成(CI)、共享 debug keystore、共享代码风格配置

这些差异并非矛盾,而是 Android 生态在数年间演进的真实写照:多 dex(65k 限制的缓解)、Kotlin 普及、官方模拟器性能提升,都让早期的一些规避性建议变得不再必要。读者在落地本指南时,建议以英文原版 README.md 为"最新基线",以本文为"历史脉络与原理说明",两者结合做出判断。

十六、致谢与许可

本指南内容源自 Futurice 开发者们的知识分享(Antti Lammi、Joni Karppinen、Peter Tackage、Timo Tuominen、Vera Izrailit、Vihtori Mäntylä、Mark Voit、Andre Medeiros、Paul Houghton 等),仓库文档与 LICENSE 表明其采用Creative Commons Attribution 4.0 International (CC BY 4.0)许可发布。仓库为只读镜像,本文仅介绍查阅与学习方式:直接阅读 README.md 及各语言翻译(如 中文版、土耳其语版)即可获得完整实践清单。

  • 文档
  • 教程
  • 移动开发

【免费下载链接】android-best-practices

Do's and Don'ts for Android development, by Futurice developers

项目地址:https://gitcode.com/gh_mirrors/an/android-best-practices
点击查看免费下载

相关推荐

上一篇:OGX Responses API 完全指南:生产可用的 OpenAI 兼容服务端 Agent 编排
下一篇:3分钟解锁网易云音乐:免费NCM转MP3终极指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询