From 12e01d965919fa821a0f10ef16eaf2977246fe47 Mon Sep 17 00:00:00 2001
From: Garciaa94 <40004257+Garciaa94@users.noreply.github.com>
Date: Sat, 5 Jun 2021 00:29:24 -0500
Subject: [PATCH] README.cn.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
# Android 开发最佳实践
//Add version of android pls 🩹
从[Futurice](http://www.futurice.com)公司Android开发者中学到的经验。
遵循以下准则,避免重复发明轮子。若你对开发iOS或Windows Phone 有兴趣,
请看[**iOS Good Practices**](https://github.com/futurice/ios-good-practices) 和 [**Windows client Good Practices**](https://github.com/futurice/win-client-dev-good-practices) 这两篇文章。
## 摘要
* 使用 Gradle 和它推荐的工程结构
* 把密码和敏感数据放在gradle.properties
* 不要自己写 HTTP 客户端,使用Volley或OkHttp库
* 使用Jackson库解析JSON数据
* 避免使用Guava同时使用一些类库来避免*65k method limit*(一个Android程序中最多能执行65536个方法)
* 使用 Fragments来呈现UI视图
* 使用 Activities 只是为了管理 Fragments
* Layout 布局是 XMLs代码,组织好它们
* 在layoutout XMLs布局时,使用styles文件来避免使用重复的属性
* 使用多个style文件来避免单一的一个大style文件
* 保持你的colors.xml 简短DRY(不要重复自己),只是定义调色板
* 总是使用dimens.xml DRY(不要重复自己),定义通用常数
* 不要做一个深层次的ViewGroup
* 在使用WebViews时避免在客户端做处理,当心内存泄露
* 使用Robolectric单元测试,Robotium 做UI测试
* 使用Genymotion 作为你的模拟器
* 总是使用ProGuard 和 DexGuard混淆来项目
### Android SDK
将你的[Android SDK](https://developer.android.com/sdk/installing/index.html?pkg=tools)放在你的home目录或其他应用程序无关的位置。
当安装有些包含SDK的IDE的时候,可能会将SDK放在IDE同一目录下,当你需要升级(或重新安装)IDE或更换的IDE时,会非常麻烦。
此外,如果你的IDE是在普通用户下运行,而不是在root下运行,还要避免把SDK放到一下需要sudo权限的系统级别目录下。
### 构建系统
你的默认编译环境应该是[Gradle](http://tools.android.com/tech-docs/new-build-system).
Ant 有很多限制,也很冗余。使用Gradle,完成以下工作很方便:
- 构建APP不同版本的变种
- 制作简单类似脚本的任务
- 管理和下载依赖
- 自定义秘钥
- 更多
同时,Android Gradle插件作为新标准的构建系统正在被Google积极的开发。
### 工程结构
有两种流行的结构:老的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
```
主要的区别在于,新的结构明确的分开了'source sets' (`main`,`androidTest`),这是Gradle的一个理念。
通过这个你可以做到,例如,添加源组‘paid’和‘free’在src中,让你的应用程序具有付费和免费的两种模式的源代码。
你的项目引用第三方项目库时(例如,library-foobar),拥有一个顶级包名`app`从第三方库项目区分你的应用程序是非常有用的。
然后`settings.gradle`不断引用这些库项目,其中`app/build.gradle`可以引用。
### Gradle 配置
**常用结构** 参考[Google's guide on Gradle for Android](http://tools.android.com/tech-docs/new-build-system/user-guide)
**小任务** 除了(shell, Python, Perl, etc)这些脚本语言,你也可以使用Gradle 制作任务。
更多信息请参考[Gradle's documentation](http://www.gradle.org/docs/current/userguide/userguide_single.html#N10CBF)。
**密码** 在做版本release时你app的 `build.gradle`你需要定义 `signingConfigs`.此时你应该避免以下内容:
_不要做这个_ . 这会出现在版本控制中。
```groovy
signingConfigs {
release {
storeFile file("myapp.keystore")
storePassword "password123"
keyAlias "thekey"
keyPassword "password789"
}
}
```
而是,建立一个不加入版本控制系统的`gradle.properties`文件。
```
KEYSTORE_PASSWORD=password123
KEY_PASSWORD=password789
```
那个文件是gradle自动引入的,你可以在`buld.gradle`文件中使用,例如:
```groovy
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.")
}
}
}
```
**使用 Maven 依赖方案代替使用导入jar包方案** 如果在你的项目中你明确使用某些
jar文件,那么它们可能成为固定的版本,如`2.1.1`.下载jar包更新他们是很繁琐的,
这个问题Maven很好的解决了,这在Android Gradle构建中也是推荐的方法。你可
以指定版本的一个范围,如`2.1.+`,然后Maven会自动升级到制定的最新版本,例如:
```groovy
dependencies {
implementation 'com.netflix.rxjava:rxjava-core:0.19.+'
implementation 'com.netflix.rxjava:rxjava-android:0.19.+'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.4.+'
implementation 'com.fasterxml.jackson.core:jackson-core:2.4.+'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.4.+'
implementation 'com.squareup.okhttp:okhttp:2.0.+'
implementation 'com.squareup.okhttp:okhttp-urlconnection:2.0.+'
}
```
### IDEs and text editors
### IDE集成开发环境和文本编辑器
**无论使用什么编辑器,一定要构建一个良好的工程结构。** 编辑器每个人都有自己的
选择,让你的编辑器根据工程结构和构建系统运作,那是你自己的责任。
当下首推[Android Studio](https://developer.android.com/sdk/installing/studio.html),因为他是由谷歌开发,很好地支持Gradle,包含很多有用的检测和分析工具,默认使用最新的工程结构,它就是为Android开发定制的。
你也可以使用纯文版编辑器如Vim,Sublime Text,或者Emacs。如果那样的话,你需要使用Gradle和`adb`命令行。
不再推荐使用Eclipse和ADT开发,因为[谷歌在2015年年末结束了对ADT的支持](https://android-developers.googleblog.com/2015/06/an-update-on-eclipse-android-developer.html),并呼吁开发者尽快迁移到Android Studio。
无论你使用何种开发工具,避免将你的编辑器配置文件(比如Android Studio的iml文件)加入到版本控制,因为这些文件通常包含与本地机器有关的配置,可能会影响你的同事。
最后,善待其他开发者,不要强制改变他们的开发工具和偏好。
### 类库
**[Jackson](http://wiki.fasterxml.com/JacksonHome)** 是一个将java对象转换成JSON与JSON转化java类的类库。[Gson](https://code.google.com/p/google-gson/)
是解决这个问题的流行方案,然而我们发现Jackson更高效,因为它支持替代的方法处理JSON:流、内存树模型,和传统JSON-POJO数据绑定。不过,请记住,
Jsonkson库比起GSON更大,所以根据你的情况选择,你可能选择GSON来避免APP 65k个方法的限制。其它选择: [Json-smart](https://code.google.com/p/json-smart/) and [Boon JSON](https://github.com/RichardHightower/boon/wiki/Boon-JSON-in-five-minutes)
**网络请求,缓存,图片** 执行请求后端服务器,有几种交互的解决方案,你应该考虑实现你自己的网络客户端。使用 [Volley](https://android.googlesource.com/platform/frameworks/volley)
或[Retrofit](http://square.github.io/retrofit/)。Volley 同时提供图片缓存类。如果你选择使用Retrofit,那么考虑使用[Picasso](http://square.github.io/picasso/)
来加载图片和缓存,同时使用[OkHttp](http://square.github.io/okhttp/)作为高效的网络请求。Retrofit,Picasso和OkHttp都是同一家公司开发(注:
是由[Square](https://github.com/square) 公司开发),所以它们能很好的在一起运行。[OkHttp 同样可以和Volley在一起使用 Volley](http://stackoverflow.com/questions/24375043/how-to-implement-android-volley-with-okhttp-2-0/24951835#24951835).
**RxJava** 是函数式反应性的一个类库,换句话说,能处理异步的事件。
这是一个强大的和有前途的模式,同时也可能会造成混淆,因为它是如此的不同。
我们建议在使用这个库架构整个应用程序之前要谨慎考虑。
有一些项目是使用RxJava完成的,如果你需要帮助可以跟这些人取得联系:
Timo Tuominen, Olli Salonen, Andre Medeiros, Mark Voit, Antti Lammi, Vera Izrailit, Juha Ristolainen.
我们也写了一些博客:
[[1]](http://blog.futurice.com/tech-pick-of-the-week-rx-for-net-and-rxjava-for-android), [[2]](http://blog.futurice.com/top-7-tips-for-rxjava-on-android),
[[3]](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754),
[[4]](http://blog.futurice.com/android-development-has-its-own-swift).
如若你之前有使用过Rx的经历,开始从API响应应用它。
另外,从简单的UI事件处理开始运用,如单击事件或在搜索栏输入事件。
若对你的Rx技术有信心,同时想要将它应用到你的整体架构中,那么请在复杂的部分写好Javadocs文档。
请记住其他不熟悉RxJava的开发人员,可能会非常难理解整个项目。
尽你的的全力帮助他们理解你的代码和Rx。
**[Retrolambda](https://github.com/evant/gradle-retrolambda)** 是一个在Android和预JDK8平台上的使用Lambda表达式语法的Java类库。
它有助于保持你代码的紧凑性和可读性,特别当你使用如RxJava函数风格编程时。
使用它时先安装JDK8,在Android Studio工程结构对话框中把它设置成为SDK路径,同时设置`JAVA8_HOME`和`JAVA7_HOME`环境变量,
然后在工程根目录下配置 build.gradle:
```groovy
dependencies {
classpath 'me.tatarka:gradle-retrolambda:2.4.+'
}
```
同时在每个module 的build.gradle中添加
```groovy
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 提供Java8 lambdas表带是代码提示支持。如果你对lambdas不熟悉,只需参照以下开始学习吧:
- 任何只包含一个接口的方法都是"lambda friendly"同时代码可以被折叠成更紧凑的语法
- 如果对参数或类似有疑问,就写一个普通的匿名内部类,然后让Android Studio 为你生成一个lambda。
**当心dex方法数限制,同时避免使用过多的类库** Android apps,当打包成一个dex文件时,有一个65535个应用方法强硬限制[[1]](https://medium.com/@rotxed/dex-skys-the-limit-no-65k-methods-is-28e6cb40cf71) [[2]](http://blog.persistent.info/2014/05/per-package-method-counts-for-androids.html) [[3]](http://jakewharton.com/play-services-is-a-monolith/)。
当你突破65k限制之后你会看到一个致命错误。因此,使用一个正常范围的类库文件,同时使用[dex-method-counts](https://github.com/mihaip/dex-method-counts)
工具来决定哪些类库可以再65k限制之下使用,特别的避免使用Guava类库,因为它包含超过13k个方法。
### Activities and Fragments
[Fragments](http://developer.android.com/guide/components/fragments.html)应该作为你实现UI界面默认选择。你可以重复使用Fragments用户接口来
组合成你的应用。我们强烈推荐使用Fragments而不是activity来呈现UI界面,理由如下:
- **提供多窗格布局解决方案** Fragments 的引入主要将手机应用延伸到平板电脑,所以在平板电脑上你可能有A、B两个窗格,但是在手机应用上A、B可能分别充满
整个屏幕。如果你的应用在最初就使用了fragments,那么以后将你的应用适配到其他不同尺寸屏幕就会非常简单。
- **屏幕间数据通信** 从一个Activity发送复杂数据(例如Java对象)到另外一个Activity,Android的API并没有提供合适的方法。不过使用Fragment,你可以使用
一个activity实例作为这个activity子fragments的通信通道。即使这样比Activity与Activity间的通信好,你也想考虑使用Event Bus架构,使用如
[Otto](https://square.github.io/otto/) 或者 [greenrobot EventBus](https://github.com/greenrobot/EventBus)作为更简洁的实现。
如果你希望避免添加另外一个类库,RxJava同样可以实现一个Event Bus。
- **Fragments 一般通用的不只有UI** 你可以有一个没有界面的fragment作为Activity提供后台工作。
进一步你可以使用这个特性来创建一个[fragment 包含改变其它fragment的逻辑](http://stackoverflow.com/questions/12363790/how-many-activities-vs-fragments/12528434#12528434)
而不是把这个逻辑放在activity中。
- **甚至ActionBar 都可以使用内部fragment来管理** 你可以选择使用一个没有UI界面的fragment来专门管理ActionBar,或者你可以选择使用在每个Fragment中
添加它自己的action 来作为父Activity的ActionBar.[参考](http://www.grokkingandroid.com/adding-action-items-from-within-fragments/).
很不幸,我们不建议广泛的使用嵌套的[fragments](https://developer.android.com/about/versions/android-4.2.html#NestedFragments),因为
有时会引起[matryoshka bugs](http://delyan.me/android-s-matryoshka-problem/)。我们只有当它有意义(例如,在水平滑动的ViewPager在
像屏幕一样fragment中)或者他的确是一个明智的选择的时候才广泛的使用fragment。
在一个架构级别,你的APP应该有一个顶级的activity来包含绝大部分业务相关的fragment。你也可能还有一些辅助的activity ,这些辅助的activity与主activity
通信很简单限制在这两种方法
[`Intent.setData()`](http://developer.android.com/reference/android/content/Intent.html#setData(android.net.Uri)) 或 [`Intent.setAction()`](http://developer.android.com/reference/android/content/Intent.html#setAction(java.lang.String))或类似的方法。
### Java 包结构
Android 应用程序在架构上大致是Java中的[Model-View-Controller](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)结构。
在Android 中 Fragment和Activity通常上是控制器类(http://www.informit.com/articles/article.aspx?p=2126865).
换句话说,他们是用户接口的部分,同样也是Views视图的部分。
正是因为如此,才很难严格的将fragments (或者 activities) 严格的划分成 控制器controlloers还是视图 views。
最还是将它们放在自己单独的 `fragments` 包中。只要你遵循之前提到的建议,Activities 则可以放在顶级目录下。
如果你规划有2到3个以上的activity,那么还是同样新建一个`activities`包吧。
然而,这种架构可以看做是另一种形式的MVC,
包含要被解析API响应的JSON数据,来填充的POJO的`models`包中。
和一个`views`包来包含你的自定义视图、通知、导航视图,widgets等等。
适配器Adapter是在数据和视图之间。然而他们通常需要通过`getView()`方法来导出一些视图,
所以你可以将`adapters`包放在`views`包里面。
一些控制器角色的类是应用程序级别的,同时是接近系统的。
这些类放在`managers`包下面。
一些繁杂的数据处理类,比如说"DateUtils",放在`utils`包下面。
与后端交互负责网络处理类,放在`network`包下面。
总而言之,以最接近用户而不是最接近后端去安排他们。
```
com.futurice.project
├─ network
├─ models
├─ managers
├─ utils
├─ fragments
└─ views
├─ adapters
├─ actionbar
├─ widgets
└─ notifications
```
### 资源文件 Resources
- **命名** 遵循前缀表明类型的习惯,形如`type_foo_bar.xml`。例如:`fragment_contact_details.xml`,`view_primary_button.xml`,`activity_main.xml`.
**组织布局文件** 如果你不确定如何排版一个布局文件,遵循一下规则可能会有帮助。
- 每一个属性一行,缩进4个空格
- `android:id` 总是作为第一个属性
- `android:layout_****` 属性在上边
- `style` 属性在底部
- 关闭标签`/>`单独起一行,有助于调整和添加新的属性
- 考虑使用[Designtime attributes 设计时布局属性](http://tools.android.com/tips/layout-designtime-attributes),Android Studio已经提供支持,而不是硬编码`android:text`
(译者注:墙内也可以参考stormzhang的这篇博客[链接](http://stormzhang.com/devtools/2015/01/11/android-studio-tips1/))。
```xml
```
作为一个经验法则,`android:layout_****`属性应该在 layout XML 中定义,同时其它属性`android:****` 应放在 styler XML中。此规则也有例外,不过大体工作
的很好。这个思想整体是保持layout属性(positioning, margin, sizing) 和content属性在布局文件中,同时将所有的外观细节属性(colors, padding, font)放
在style文件中。
例外有以下这些:
- `android:id` 明显应该在layout文件中
- layout文件中`android:orientation`对于一个`LinearLayout`布局通常更有意义
- `android:text` 由于是定义内容,应该放在layout文件中
- 有时候将`android:layout_width` 和 `android:layout_height`属性放到一个style中作为一个通用的风格中更有意义,但是默认情况下这些应该放到layout文件中。
**使用styles** 几乎每个项目都需要适当的使用style文件,因为对于一个视图来说有一个重复的外观是很常见的。
在应用中对于大多数文本内容,最起码你应该有一个通用的style文件,例如:
```xml
```
应用到TextView 中:
```xml
```
你或许需要为按钮控件做同样的事情,不要停止在那里。将一组相关的和重复`android:****`的属性放到一个通用的style中。
**将一个大的style文件分割成多个文件** 你可以有多个`styles.xml` 文件。Android SDK支持其它文件,`styles`这个文件名称并没有作用,起作用的是在文件
里xml的`