文档教程移动开发【免费下载链接】android-best-practicesDos and Donts for Android development, by Futurice developers项目地址https://gitcode.com/gh_mirrors/an/android-best-practices点击查看免费下载本文基于本仓库中的法语版官方指南 translations/French/README.fr.md 编译整理并对照仓库根目录的英文原版 README.md 进行补充印证。这是一份由 Futurice 公司 Android 开发者多年实战沉淀的做与不做清单覆盖构建系统、工程结构、依赖选型、Activity/Fragment 架构、资源文件组织、模拟器与代码混淆等完整链路。读完本文你将掌握一套可直接落地的 Android 工程化规范如何配置 Gradle 与签名、如何组织包结构与 XML 资源、如何规避 65k 方法数与深层视图层级等经典陷阱以及如何用 ProGuard 安全地完成发布包瘦身与混淆。本指南源自 Futurice 公司 Android 开发者们的实战经验总结核心宗旨是不要重复发明轮子Avoid reinventing the wheel。原文档同时提供了中文、日文、韩文、西班牙文、法文、德文等多种翻译版本见 translations 目录本文以法语版为骨架展开并吸收英文原版中的最新建议作为补充。一、Android SDK安装位置与权限考量把 Android SDK 安装在用户主目录home或其它与应用无关的独立位置这是第一条环境层面的最佳实践。很多 IDE 在安装时会自带 SDK并把它放到与 IDE 相同的目录下。这会带来两个隐患当你需要升级或重新安装 IDE时SDK 可能随之一并丢失被迫重新下载当你更换 IDE时SDK 与旧 IDE 深度绑定迁移成本很高。此外还要避免把 SDK 放在系统级目录中——如果你的 IDE 不是以管理员root身份运行放在需要管理员权限的目录里会引发持续的权限问题。正确的做法是让 SDK 独立于任何 IDE 存在这样无论工具链如何更换SDK 本身都能保持稳定。二、构建系统以 Gradle 为默认方案项目的默认构建系统应当是Gradle配合 Google 的 Android Gradle 插件。原文档明确指出Ant 构建系统功能受限且写法繁琐Ant est bien plus limité et aussi plus verbeux而 Gradle 可以轻松实现构建应用的不同flavors风味变体编写实现自定义任务的脚本管理和下载依赖自定义签名密钥Store Keys以及更多能力。同时Google 正在积极开发 Android Gradle 插件其目标是成为新一代构建系统的标准。英文原版 README.md 还补充了一条关键原则应用的构建过程必须由 Gradle 文件定义而不是依赖 IDE 特有的配置。这样能保证不同工具之间构建结果一致并为持续集成CI系统提供良好支持。三、工程结构从旧结构迁移到新结构Android 工程存在两种流行的结构旧式 Ant Eclipse ADT 结构与新式 Gradle Android Studio 结构。官方建议直接采用新结构如果项目还在用旧结构应当尽早迁移。旧结构ancienne-structure旧结构 ├─ assets ├─ libs ├─ res ├─ src │ └─ com/futurice/project ├─ AndroidManifest.xml ├─ build.gradle ├─ project.properties └─ proguard-rules.pro新结构nouvelle-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两种结构最核心的区别在于新结构显式地区分了 source sets源码集如main、androidTest这是 Gradle 的重要概念。借助源码集你可以轻松扩展出payant付费版与gratuit免费版等不同的源码目录让同一套代码工程同时承载多个产品变体。同时新结构中的顶层app目录可以清晰地把你的主应用与其它被引用的库例如library-foobar区分开来settings.gradle记录这些库的引用之后在app/build.gradle中即可直接引用它们。英文原版 README.md 补充强调尽管 Gradle 对工程结构提供了很大的灵活性但除非有充分理由否则应当接受其默认结构这能简化构建脚本。四、Gradle 配置实战4.1 总体结构总体配置遵循 Google 官方的 Gradle for Android 指南。英文原版 README.md 额外建议将minSdkVersion设为 21Android 5.0理由是部分 Material Design 特性仅在 API 21 及以上可用且从 API 21 起不再需要 multidex 支持库。当然设定最低版本前应参考 Android 版本使用情况统计并注意这些是全球统计值针对特定区域/人群市场时可能不同。4.2 小任务用 Gradle 任务替代外部脚本与其为构建流程编写 shell、Python、Perl 等外部脚本不如把这些逻辑写成Gradle 任务。这样构建流程完全由 Gradle 文件定义不依赖外部环境参考 Gradle 官方文档即可掌握任务编写方法。4.3 签名密码永远放进gradle.properties发布Release构建需要在build.gradle中定义signingConfigs。以下写法必须避免——明文密码会进入版本控制系统如 Git造成严重的安全隐患// 不要这样做密码会出现在版本控制系统中 signingConfigs { release { storeFile file(myapp.keystore) storePassword password123 keyAlias thekey keyPassword password789 } }正确做法创建一个不应加入版本控制的gradle.properties文件KEYSTORE_PASSWORDpassword123 KEY_PASSWORDpassword789Gradle 会自动导入这个文件因此在build.gradle中可以这样引用并加上健壮的异常处理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.) } } }当密码变量缺失时构建会立即抛出InvalidUserDataException并给出明确提示而不是在发布阶段才暴露问题。gradle.properties应加入.gitignore确保敏感信息永不入库。4.4 优先使用 Maven 依赖解析而非导入 jar 文件如果显式把.jar文件放进项目它们会被冻结在某个特定版本例如2.1.1后续下载新版本、维护升级都是沉重且繁琐的工作而 Maven 依赖解析已经优雅地解决了这个问题。例如dependencies { implementation com.squareup.okhttp:okhttp:2.2.0 implementation com.squareup.okhttp:okhttp-urlconnection:2.2.0 }4.5 避免 Maven 动态依赖版本不要使用2.1.这类动态版本号它可能导致构建不稳定或在不同构建之间产生难以追踪的细微行为差异。使用2.1.1这样的静态版本号才能创造更稳定、可预测、可复现的开发环境。英文原版 README.md 还提供了两条值得借鉴的补充实践为 debug 构建使用独立的包名通过applicationIdSuffix给 debug 构建追加.debug后缀并给versionName追加-DEBUG后缀这样 debug 与 release 的 APK 可以同时安装在同一台设备上对发布后的线上问题排查尤其有价值同时可以为不同构建类型放置不同图标默认结构下 debug 图标放在app/src/debug/resrelease 图标放在app/src/release/res共享 debug 签名 keystoredebug 密钥文件可以安全地放入代码仓库便于团队成员在同一设备上测试时免于反复卸载/重装也简化了 Facebook 等要求注册单一 keystore 哈希的 SDK 接入流程。五、开发工具IDE 与编辑器选择你可以使用任何编辑器前提是它必须兼容工程的 Gradle 结构与构建系统。工具选择是个人自由但责任在自己——务必选择能配合工程结构和构建系统的工具。当前最受推荐的 IDE 是Android Studio它由 Google 开发、与 Gradle 深度集成、默认使用新工程结构、已经稳定并且是专门为 Android 开发设计的。英文原版补充道Android Studio 还内置了大量监控与分析工具并且 Google 在持续更新它。如果你仍在使用Eclipse ADT需要知道它默认采用旧工程结构和 Ant 构建因此必须自行配置改走 Gradle 与命令行adb的路线。若 Gradle 集成在 Eclipse 中无法正常工作可选方案是只用命令行构建或者干脆迁移到 Android Studio——后者是更好的选择因为 ADT 插件已经停止维护deprecated。无论选择什么工具请确保使用新工程结构、使用 Gradle、并且不要把编辑器特有的配置文件例如 Ant 的build.xml提交到版本控制系统。变更构建配置时记得同步更新build.gradle。最后一条职场建议善待团队中的其他开发者不要强迫他们更换开发工具或偏好。六、第三方库选型6.1 JSON 解析Jackson 与 GsonJackson是一个能够在 Java 对象与 JSON 之间互相转换的库。它与Gson功能相似且同样流行但 Futurice 团队认为 Jackson 更强大因为它支持多种 JSON 处理方式流式处理streaming内存树模型tree model传统的JSON-POJO 数据绑定data binding。不过 Jackson 的体积比 Gson 更大在需要规避 65k 方法数限制的场景下可能反而要选择更小的 Gson。其它可选方案还包括 Json-smart 与 Boon JSON。英文原版 README.md 还提到了 Square 出品的Moshi——它建立在 Gson 的开发经验之上并且与 Kotlin 集成良好。6.2 网络、缓存与图片加载做后端请求有多种经过实战验证的成熟方案不要自己编写 HTTP 客户端VolleyGoogle 出品的网络库还附带图片加载与缓存的辅助工具Retrofit如果选择它建议配合Picasso做图片加载与缓存、OkHttp做高性能 HTTP 请求。Retrofit、Picasso、OkHttp 三件套出自同一家公司Square彼此互补、兼容性问题少OkHttp 也可以与 Volley 结合使用。英文原版 README.md 的推荐更聚焦以OkHttp为基础做高效 HTTP 请求用Retrofit提供类型安全typesafe的 API 层图片加载除 Picasso 外也可考虑Glide支持 GIF 动画、圆形图片性能声称更好但方法数更多。6.3 RxJava响应式编程的引入节奏RxJava是用于响应式编程Reactive Programming的库本质上是处理异步事件。这是一个强大但有学习曲线的范式官方建议在用它构建整个应用架构之前保持谨慎。Futurice 团队的部分项目已用 RxJava 实现如果你需要帮助可以联系文档中列出的团队成员Timo Tuominen、Olli Salonen、Andre Medeiros 等团队还撰写过多篇相关技术博客。如果此前没有 Rx 经验建议的引入路线是先只把它用在后端 API 响应的处理上或者先用于简单的UI 事件例如按钮点击、搜索框输入只有当你的 Rx 技能足够自信、并决定把它应用于整体架构时才全面推进——但务必为所有难懂的部分编写 Javadoc。请记住一个不熟悉 RxJava 的后来维护者会非常吃力你的职责是尽量帮助他们理解代码与 Rx 范式。英文原版还建议配套使用RxAndroidAndroid 线程调度支持与RxBinding从现有 Android 组件便捷地创建 Observable。6.4 Retrolambda在旧 JDK 上使用 Lambda 语法Retrolambda是用于在 Android 及其它 JDK8 之前平台上使用 Lambda 表达式语法的 Java 库。它能保持代码简洁易读尤其适合配合 RxJava 这类函数式风格编程。使用步骤安装 JDK8并在 Android Studio 项目中将其设为默认 SDK配置环境变量JAVA8_HOME与JAVA7_HOME在项目的build.gradle中添加插件依赖dependencies { classpath me.tatarka:gradle-retrolambda:2.4.1 }在每个模块的build.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 不再是必需品官方已原生支持部分 Java 8 特性。本文保留 Retrolambda 章节因为它仍是老项目中的常见实践且上述配置方式至今仍有参考价值。6.5 警惕 65k 方法数限制慎用 GuavaAndroid 应用打包成 Dex 文件后存在一个硬性上限65536 个被引用的方法。一旦超过这个限制编译会直接报致命错误。因此尽量少引入第三方库使用dex-method-counts工具统计各库的方法数判断哪组库可以组合使用而不超限尤其避免使用 Guava——它包含超过 1.3 万个方法。英文原版 README.md 进一步提醒Google Play Services 本身也是一个庞大的方法数来源play-services-is-a-monolith做依赖裁剪时值得专门关注。从 API 21 起 multidex 支持库不再是必需品这也是前文建议minSdkVersion: 21的动机之一。七、Activity 与 Fragment架构取舍无论是社区还是 Futurice 内部对于如何用 Activity 与 Fragment 组织 Android 架构都没有统一共识。Square 甚至推出了一个主要基于 View 构建架构的库mortar从而绕过 Fragment但这并未被社区广泛推荐。由于 Android API 的历史原因可以粗略地把Fragment 理解为屏幕上的 UI 片段通常与界面相关而把Activity 理解为控制器——它特别重要地承担了生命周期管理与状态管理职责。不过角色经常会有变化Activity 也可能承担 UI 角色例如在屏幕间转场Fragment 也可能被单独用作控制器无 UI 的 Fragment。官方建议谨慎决策、充分了解每种方案的利弊纯 Fragment、纯 Activity、纯 View 三种路线都有各自的缺点。以下是几条需要保持警惕的建议避免滥用嵌套 Fragment过度嵌套可能触发 套娃 bugmatryoshka problem。仅在确实有意义时使用嵌套例如在横向滑动的 ViewPager 中放置 Fragment或者在你完全清楚后果的情况下避免在 Activity 中放过多代码尽量让 Activity 保持轻量只作为容器主要用于生命周期和与 Android 系统交互的重要 API。优先使用单 Fragment Activity而非裸 Activity——把 UI 代码放进 Activity 对应的 Fragment 中。这样当你要把它放进带标签页的布局或放进平板的双 Fragment 屏幕时代码可以直接复用。除非你做了充分决策否则避免存在没有对应 Fragment 的 Activity不要滥用 Intent 等系统 API 做应用内部通信这可能引发 Bug 或卡顿。例如已有实践证明如果应用在手机刚开机后立即被打开用 Intent 在应用内部包之间通信可能造成长达数秒的卡顿。八、Java 包结构设计Android 应用的 Java 架构可以近似看作MVCModel-View-Controller。在 Android 中Fragment 和 Activity 既是控制器同时又是 UI 的组成部分因此也是视图。这种双重身份使得把它们单纯归类为控制器或视图都很困难。基于此原文档给出了一套按层layer划分的包结构建议Fragment以及 Activity保留在fragments包中Activity 可以留在顶层包内如果 Activity 数量超过 23 个就建一个activities包models包存放由 API 响应填充的 POJO 对象配合 JSON 解析器使用views包存放所有自定义视图、通知、ActionBar、Widget 等Adapter 介于数据与视图之间但因为它需要经由getView()导出视图可以放进views包下的adapters子包managers包存放作用域为整个应用、贴近 Android 系统的控制器utils包存放DateUtils之类的杂项数据处理类network包存放与后端交互的类。按离后端最近 → 离用户最近排序整体结构如下com.futurice.project ├─ network ├─ models ├─ managers ├─ utils ├─ fragments └─ views ├─ adapters ├─ actionbar ├─ widgets └─ notifications英文原版 README.md 已经演进为基于功能feature based的包结构建议理由是它带来更清晰的功能依赖边界、更好的封装性、更易理解的组件归属、更低的误改风险、更简单的导航、更易移除功能以及更平滑地过渡到模块化构建更好的构建时间与 Instant Apps 支持。而按如何构建把相关 Activity、Fragment、Adapter 分别放不同包的方式组织容易导致代码库碎片化、实现灵活性下降。两套方案各有适用场景老项目可参考法语版的分层方案新项目建议采用英文原版的功能化方案。九、资源文件组织规范9.1 命名规则遵循类型前缀约定type_foo_bar.xml例如fragment_contact_details.xmlview_primary_button.xmlactivity_main.xml9.2 Layout XML 的组织约定如果不确定如何组织布局 XML以下约定很有帮助每个属性单独一行用空格缩进英文原版建议 4 空格android:id永远是第一个属性android:layout_****系列属性放在顶部紧随android:idstyle属性放在最底部结束标签/单独占一行便于后续添加和排序属性与其硬编码android:text不如使用 Android Studio 提供的Design-time设计期属性。标准示例?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/android xmlns:toolshttp://schemas.android.com/tools android:layout_widthmatch_parent android:layout_heightmatch_parent android:orientationvertical TextView android:idid/name android:layout_widthmatch_parent android:layout_heightwrap_content android:layout_alignParentRighttrue android:textstring/name stylestyle/FancyText / include layoutlayout/reusable_part / /LinearLayout通用规则android:layout_****属性应放在布局 XML 中其余android:****属性放进样式 XML。也就是说布局文件只保留布局位置、边距、尺寸与内容属性而所有外观细节颜色、内边距、字体都放进样式文件。例外情况android:id显然必须在布局文件里LinearLayout的android:orientation通常放在布局文件里更合理android:text定义的是内容应放在布局文件里偶尔用一个通用样式统一设置android:layout_width和android:layout_height也是合理的但默认情况下它们应当出现在布局文件中。9.3 使用 Styles 消除重复属性几乎所有项目都需要正确使用样式Styles因为视图外观重复非常常见。至少应为应用中大部分文本内容定义一个通用样式例如style nameContentText item nameandroid:textSizedimen/font_normal/item item nameandroid:textColorcolor/basic_black/item /style应用到 TextViewTextView android:layout_widthwrap_content android:layout_heightwrap_content android:textstring/price stylestyle/ContentText /按钮通常也需要类似处理。但不要止步于此——更进一步把一组相关的、重复出现的android:****属性整体收敛到通用样式中。9.4 拆分大型样式文件你完全不必只有一个styles.xml。Android SDK 原生支持多个样式文件styles这个名字本身没有任何魔法真正重要的是文件内部的style标签。因此可以这样拆分styles.xmlstyles_home.xmlstyles_item_details.xmlstyles_forms.xml与资源目录名不同目录名对构建系统有特定含义res/values目录下的文件名可以是任意的。9.5colors.xml只做调色板colors.xml中除了颜色名 → RGBA 值的映射之外不应该有任何别的东西。不要用它去定义各种按钮类型。不要这样做——带上下文的命名如 button、comment难以复用改一个基础色要牵动多处而且这些上下文本身应该体现在按钮样式中而非颜色文件里resources color namebutton_foreground#FFFFFF/color color namebutton_background#2A91BD/color color namecomment_background_inactive#5F5F5F/color color namecomment_background_active#939393/color color namecomment_foreground#FFFFFF/color color namecomment_foreground_important#FF9D2F/color ... color namecomment_shadow#323232/color /resources应该这样做——只维护一份干净的调色板resources !-- grayscale 灰度色 -- color namewhite #FFFFFF/color color namegray_light#DBDBDB/color color namegray #939393/color color namegray_dark #5F5F5F/color color nameblack #323232/color !-- basic colors 基础色 -- color namegreen#27D34D/color color nameblue#2A91BD/color color nameorange#FF9D2F/color color namered#FF432F/color /resources这份调色板可以直接向设计师索取。颜色名不一定是 green、blue 这种字面颜色名像brand_primary、brand_secondary、brand_negative这样的语义化命名也完全可以接受。把颜色这样组织之后改动颜色很容易应用实际使用了多少种颜色一目了然——通常一个好看的界面应当尽量减少颜色种类。英文原版 README.md 进一步给出了三层职责分离的进阶做法colors.xml只定义调色板 → 可选的中间层颜色资源文件把调色板映射成语义名如color/white映射为button_foreground→styles.xml中的样式引用语义色。这样多组相关样式共享相似颜色与用途时重构更安全、样式定义更稳定代价是需要多维护一套颜色映射。9.6dimens.xml像 colors.xml 一样保持 DRY同样地应该为应用中典型的间距与字号定义一套维度调色板。一个良好的dimens.xml示例resources !-- font sizes 字号 -- dimen namefont_larger22sp/dimen dimen namefont_large18sp/dimen dimen namefont_normal15sp/dimen dimen namefont_small12sp/dimen !-- typical spacing between two views 视图间典型间距 -- dimen namespacing_huge40dp/dimen dimen namespacing_large24dp/dimen dimen namespacing_normal14dp/dimen dimen namespacing_small10dp/dimen dimen namespacing_tiny4dp/dimen !-- typical sizes of views 视图典型尺寸 -- dimen namebutton_height_tall60dp/dimen dimen namebutton_height_normal40dp/dimen dimen namebutton_height_short32dp/dimen /resources在布局、边距margin与内边距padding中应当使用spacing_****这类维度常量而不是硬编码数值——就像字符串资源那样对待维度。这会给应用带来一致的视觉感受同时让样式与布局的组织和修改都更加容易。9.7strings.xml命名空间式命名字符串的 key 应当采用类似命名空间的形式并且不要害怕为两三个 key 复用同一个值——因为命名空间是消除歧义、提供上下文所必需的。不推荐string namenetwork_errorErreur de réseau/string string namecall_failedLappel a échoué/string string namemap_failedLe chargement de la carte a échoué/string推荐string nameerror.message.networkErreur de réseau/string string nameerror.message.callLappel a échoué/string string nameerror.message.mapLe chargement de la carte a échoué/string另一个硬性规则不要把字符串值写成全大写遵循正常的文本大小写惯例例如首字母大写。如果确实需要全大写显示请在 TextView 上使用textAllCaps属性来完成不推荐string nameerror.message.callLAPPEL A ECHOUE/string推荐string nameerror.message.callLappel a échoué/string英文原版 README.md 使用下划线风格error_message_network两种分隔风格均可关键是保持命名空间式的语义层级。9.8 避免过深的视图层级有时你会忍不住为了某个排列效果再套一层 LinearLayout。这种嵌套可能不断蔓延LinearLayout android:layout_widthmatch_parent android:layout_heightmatch_parent android:orientationvertical RelativeLayout ... LinearLayout ... LinearLayout ... LinearLayout ... /LinearLayout /LinearLayout /LinearLayout /RelativeLayout /LinearLayout即使你没有在布局文件中显式看到这种结构当你在 Java 代码里把视图 inflate 进其它视图时同样可能发生。深层视图层级会带来两类问题一是性能问题——处理器需要遍历一棵复杂的视图树二是更严重的StackOverflowError视图过多引发的栈溢出。因此请尽量让视图层级保持扁平学会使用RelativeLayout英文原版已进一步推荐功能更强的ConstraintLayout、学习官方 optimize your layouts 布局优化指南以及合理使用merge标签来合并冗余层级。9.9 WebView 使用注意事项当必须展示网页时例如新闻文章避免在客户端做 HTML 清洗处理而应要求后端开发者直接提供一份纯净版本的 HTML 页面。WebView 还容易引发内存泄漏当它持有 Activity 的引用、而不是绑定 ApplicationContext 时会导致泄漏。此外不要用 WebView 展示简单的文本或按钮——这类需求请使用平台原生控件 TextView 与 Button。十、测试与模拟器10.1 测试框架原文档摘要中给出的组合是Robolectric 做单元测试、Robotium 做 UI图形界面测试。这是文档撰写时期的主流方案。需要说明的是英文原版 README.md 已经更新了推荐单元测试使用JUnit纯 JVM 上的无 Android 依赖测试UI 测试使用Espresso断言库推荐AssertJ-AndroidAssertJ 的扩展覆盖 Android Support、Google Play Services、Appcompat 等组件的断言。英文原版同时明确不建议再用 Robolectric——因为它通过 mock Android 平台实现脱离设备测试测试结果不准确也不完整更好的组合是 JVM 单元测试 真机/设备上的集成测试。AssertJ-Android 风格的断言示例// Example assertion using AssertJ-Android assertThat(layout).isVisible() .isVertical() .hasChildCount(5);注法语版文档对应的测试章节内容较少以上为对照英文原版 README.md 的补充。10.2 模拟器Genymotion如果你以 Android 开发为职业原文档建议购买Genymotion模拟器的许可证。其优势包括比 AVD 虚拟机刷新率更高、运行更流畅内置演示工具可模拟不同类型的网络连接、模拟 GPS 位置等非常适合做已连接设备connected测试支持大量虽非全部不同机型购买许可证的成本远低于购置同等数量的真机。缺点也很明确Genymotion 模拟器不包含全部 Google 服务如 Google Play Store、Google Maps而且如果需要测试三星手机特有的 API仍然必须购买真实设备。英文原版 README.md 的立场有所演进认为 Android SDK 模拟器尤其 x86 版本性能近年提升明显已足以应付大多数日常开发场景但真机验证的价值不可忽视——不必测试所有机型应聚焦于市场份额大、与你的应用最相关的设备。十一、ProGuard 配置与发布构建11.1 ProGuard 基础配置ProGuard通常用于缩减 Android 应用体积并对打包后的代码进行混淆。是否启用取决于项目配置通常会在构建 release APK 时启用buildTypes { debug { minifyEnabled false } release { signingConfig signingConfigs.release minifyEnabled true proguardFiles getDefaultProguardFile(proguard-android.txt), proguard-rules.pro } }要确定哪些代码需要保留、哪些可以删除或混淆你必须为代码指定一个或多个入口点entry points。Android 框架自带一份默认配置位于SDK_HOME/tools/proguard/proguard-android.txt使用上述配置时项目自定义规则文件my-project/app/proguard-rules.pro会自动追加到默认配置之后。11.2 启动崩溃排查usage.txt 与 mapping.txt一个非常常见的问题是即使构建命令例如assembleRelease成功且没有任何警告应用启动时仍崩溃抛出ClassNotFoundException、NoSuchFieldException或类似异常。这通常意味着两种情况之一ProGuard 删除了它认为不需要的类、枚举、方法、字段或注解ProGuard 混淆重命名了类、枚举或字段名但它正通过原名被间接使用——例如通过 Java 反射。排查方法检查app/build/outputs/proguard/release/usage.txt确认目标对象是否被删除检查app/build/outputs/proguard/release/mapping.txt确认目标对象是否被混淆重命名。11.3keep与keepnames规则为了阻止 ProGuard删除运行所必需的类或类成员在 ProGuard 配置中添加keep选项-keep class com.futurice.project.MyClass { *; }为了阻止 ProGuard混淆类或类成员添加keepnames选项-keepnames class com.futurice.project.MyClass { *; }更多示例可查阅 ProGuard 官方手册的 examples 章节。11.4 尽早构建 Release 并妥善保存 mapping.txt两条重要的发布实践在开发早期就做一次 release 构建并测试验证 ProGuard 规则是否正确保留了重要依赖。每引入一个新库或升级依赖都应该做一次 release 构建并在真机上测试 APK。不要等到应用做到 1.0 版本才做 release 构建——那时你可能收获一堆不愉快的意外而修复时间已经所剩无几为每次发布给用户的 release 保存一份mapping.txt。保留每个 release 构建的 mapping 文件当用户上报包含混淆栈轨迹的 Bug 时你才能通过反混淆定位问题。11.5 DexGuard如果需要更强大的优化与混淆工具可以考虑DexGuard——由 ProGuard 同一团队开发的商业软件。它特别擅长混淆发布代码而且还能把 Dex 文件拆分成多个文件从而绕过 65k 方法数限制。十二、致谢与许可证本指南内容由 Antti Lammi、Joni Karppinen、Peter Tackage、Timo Tuominen、Vera Izrailit、Vihtori Mäntylä、Mark Voit、Andre Medeiros、Paul Houghton 以及 Futurice 其他 Android 开发者共同贡献。本仓库采用Creative Commons Attribution 4.0 InternationalCC BY 4.0许可证版权归 Futurice Oy 所有详见仓库根目录的 LICENSE 文件。如果你还想了解同一团队在 iOS、Windows Phone 等平台上的最佳实践可在仓库首页 README.md 中查看相关指引各语言版本见 translations 目录。赞分享文档教程移动开发【免费下载链接】android-best-practicesDos and Donts for Android development, by Futurice developers项目地址https://gitcode.com/gh_mirrors/an/android-best-practices点击查看免费下载相关推荐Android 开发最佳实践Futurice 版完整指南Gradle 构建、资源组织与 ProGuard 实战Android 开发最佳实践Futurice 版完整指南Gradle 构建、资源组织与 ProGuard 实战 本指南以 Futurice 团队多年 An文档教程移动开发Android 开发最佳实践全解Futurice android-best-practices 实战指南构建、资源、测试、ProGuard 与数据存储Android 开发最佳实践全解Futurice android best practices 实战指南构建、资源、测试、ProGuard 与数据存储 导文档教程移动开发Android 最佳实践指南Futurice android-best-practices 工程化实战手册Gradle、Fragment、资源组织与 ProGuardAndroid 最佳实践指南Futurice android best practices 工程化实战手册Gradle、Fragment、资源组织与 Pro文档教程移动开发上一篇ImageNet-22k预训练ImageNet-1k微调convnext_tiny.fb_in22k_ft_in1k_384训练策略大揭秘下一篇M401a盒子刷Armbian实测S905L3闲置盒复活创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考