支付最好实施,Android最棒实践指南

日期:2019-09-16编辑作者:编程应用

Updated on 2016/2/14 更新Stetho 相关,简书markdown不支持锚 -_-||||||||||||Updated on 2016/1/15 表明谷歌对ADT的废弃态度,新增段落:对于非发布版本的构建使用不同的包名不定时同步更新原文欢迎转载,但请保留译注者链接:

Android 开发最佳实践

转载于https://github.com/futurice/android-best-practices

从Futurice公司Android开发者中学到的经验。
遵循以下准则,避免重复发明轮子。

Lessons learned from Android developers in Futurice. Avoid reinventing the wheel by following these guidelines. If you are interested in iOS or Windows Phone development, be sure to check also our iOS Good Practices and Windows App Development Best Practices documents.

摘要

  • 使用 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 放在你的home目录或是其他应用无关的位置。某些IDE安装的时候就包含了SDK,并且会将其放置在与IDE相同的目录下。当你需要升级IDE时这就成了一件坏事。同时还要避免将SDK放在另一个系统级别的目录下,那样很可能会让你在使用user权限运行IDE时需要用到sudo权限。

Android SDK

将你的Android SDK放在你的home目录或其他应用程序无关的位置。
当安装有些包含SDK的IDE的时候,可能会将SDK放在IDE同一目录下,当你需要升级(或重新安装)IDE或更换的IDE时,会非常麻烦。
此外,若果你的IDE是在普通用户,不是在root下运行,还要避免吧SDK放到一下需要sudo权限的系统级别目录下。

Build system 构建系统

你的默认选择应该是 Gradle。Ant的限制要多并且语句还更冗长。使用Gradle,能够简单做到:

  • 使用不同的flavours或variants来构建你的app
  • 制作简单的script-like的tasks
  • 管理并下载依赖
  • 自定义keystores
  • 还有更多

Android's Gradle plugin同时在被Google做为新标准构建系统积极开发中

构建系统

你的默认编译环境应该是Gradle.
Ant 有很多限制,也很冗余。使用Gradle,完成以下工作很方便:

  • 构建APP不同版本的变种
  • 制作简单类似脚本的任务
  • 管理和下载依赖
  • 自定义秘钥
  • 更多

同时,Android Gradle插件作为新标准的构建系统正在被Google积极的开发。

Project structure 项目结构

有两种广泛使用的选择:旧式的Ant & Eclipse ADT project structure,和新式的Gradle & Android Studio project structure。你应该选择新式,如果你还在使用旧式,考虑将之做为宝贵遗产并转向新式吧。

Old structure:

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

New structure:

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)。举个例子,你可以添加source sets 'paid' 和 'free' 到 src中作为构建 paid 版本 和 free 版本的代码目录。使用一个top-level app对于将你的app从那些需要引用的 库项目 (e.g., library-foobar) 中区分开来很有效。settings.gradle中写着那些 能被app/build.gradle引用的 库项目 的引用。

工程结构

有两种流行的结构:老的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 configuration Gradle配置

General structure. Follow Google's guide on Gradle for Android

Small tasks. 与这些 (shell, Python, Perl, etc) 脚本不同,你能用Gradle来安排tasks。Just follow Gradle's documentation for more details.

Passwords.在app的 build.gradle中你需要为release版本的构建定义signingConfigs,以下为需要避免的事项:

不要这样做。这些信息会出现在版本控制系统中。

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

与之对应,通过一个不会包含在版本控制系统中的gradle.properties这样做:

KEYSTORE_PASSWORD=password123KEY_PASSWORD=password789

这个文件将会被gradle自动载入,所以你能在build.gradle中像这样来使用它:

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

**使用 Maven 解决依赖而非导入 jar **如果你明确地在项目中包含特定版本的 jar(比如说 2.1.1),下载与处理 jars 的更新将会是一件笨重累赘的事,这个问题在 Maven 中被解决得很好,这也是 Android Gradle builds 所鼓励的方式。看下面这个例子:

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

避免 Maven 的动态依赖避免使用动态依赖的库版本, 像是 2.1.+ ,因为这可能会导致不同的、不稳定的构建,或是在数次构建之间表现出细微的、不可追踪的差异行为。使用静态版本像是2.1.1会创建更稳定的、可预期的和可重复的开发环境。

对于非发布版本的构建使用不同的包名debugbuild type使用applicationIdSuffix ,这能够让debug还有release版本的apk同时安装在同一部设备上(如果你有任何需要的话,还能将此技巧应用于自定义的 build 类型)。对于一个 app 的生命周期来说,当它被发布到市场之后,这一特性将变得非常有价值。

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

使用不同的icons来区分安装在设备上的不同构建版本app——比如说使用不同的色彩或是使用一个覆盖的"debug"标签。对于Gradle来说,这非常容易,你只需要将debugicon 放在app/src/debug/res,而release icon 放在 app/src/release/res。你还可以针对不同的构建版本更改应用名change app name,versionName也可以改变(就像上面这个例子做的那样)。

Gradle 配置

常用结构 参考Google's guide on Gradle for Android

小任务 除了(shell, Python, Perl, etc)这些脚本语言,你也可以使用Gradle 制作任务。
更多信息请参考Gradle's documentation。

密码 在做版本release时你app的 build.gradle你需要定义 signingConfigs.此时你应该避免以下内容:

不要做这个 . 这会出现在版本控制中。

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

而是,建立一个不加入版本控制系统的gradle.properties文件。

KEYSTORE_PASSWORD=password123
KEY_PASSWORD=password789

那个文件是gradle自动引入的,你可以在buld.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.")
        }
    }
}

使用 Maven 依赖方案代替使用导入jar包方案 如果在你的项目中你明确使用率
jar文件,那么它们可能成为永久的版本,如2.1.1.下载jar包更新他们是很繁琐的,
这个问题Maven很好的解决了,这在Android Gradle构建中也是推荐的方法。你可
以指定版本的一个范围,如2.1.+,然后Maven会自动升级到制定的最新版本,例如:

dependencies {
    compile 'com.netflix.rxjava:rxjava-core:0.19.+'
    compile 'com.netflix.rxjava:rxjava-android:0.19.+'
    compile 'com.fasterxml.jackson.core:jackson-databind:2.4.+'
    compile 'com.fasterxml.jackson.core:jackson-core:2.4.+'
    compile 'com.fasterxml.jackson.core:jackson-annotations:2.4.+'
    compile 'com.squareup.okhttp:okhttp:2.0.+'
    compile 'com.squareup.okhttp:okhttp-urlconnection:2.0.+'
}

IDEs and text editors IDE和文本编辑器

无论使用什么编辑器,它必须能针对项目结构让人愉快地使用文本编辑器是一个很个人的选择,依据项目结构和构建系统来让编辑器起到作用同时也是你的责任。

目前最推荐的IDE是 Android Studio,因为它由Google开发,与Gradle关系最紧密,默认使用新式项目结构,针对Android开发量身定做。

使用 Eclipse ADT 来进行Android开发不再是一个好的选择。2015年,谷歌终止了对ADT的支持,并催促开发者尽快向Android Studio迁徙。Google ended ADT support at the end of 2015and urges users tomigrate to Android Studioas soon as possible.你也可以继续使用它,但是需要一番配置,因为它采用旧式项目结构与Ant构建。如果 Eclipse 的 Gradle 集成令你使用得不愉快,你的选择只有使用命令行来构建。

你也能只使用一个单纯的文本编辑器像是Vim,Sublime Text, 或 Emacs。在这种情况下,你需要在命令行环境下使用 Gradle 和 adb

无论你使用什么,总得确保使用 Gradle 和新式项目结构 来构建应用,注意不要将编辑器相关的配置文件加到版本控制系统中。比如,避免添加Ant 的 build.xml 文件。如果你在 Ant 中更改配置, 一定不要忘记让build.gradle保持 up-to-date 和 functioning 。

还有,善待其他的开发者,不要强迫他们改变他们个性化的工具配置。

IDEs and text editors

Libraries 库

Jackson 是一个用于 Object 与 JSON 间相互转换的Java库。Gson 也是一个作为解决此问题广受欢迎的存在。然而我们发现 Jackson 表现更好,因为它提供了可选择的方式来处理 JSON : streaming, in-memory tree model, and traditional JSON-POJO data binding。所以Jackson 的体积会比 GSON 要大。取决于你的实际情况,你可能倾向于选择 GSON 以避免 65k 方法数限制。其他选项还有: Json-smart and Boon JSON

网络,缓存和图像.这儿有好几种经过实战检验的用于后端服务器请求的解决方案,你将使用哪一种取决于你自己将要实现的客户端。使用 Volley 或 Retrofit. Volley 额外提供了 helpers 以解决 载入和缓存图像。要是你选择 Retrofit, 考虑用 Picasso 来做这些, 同时用 OkHttp 来完成高效 HTTP 请求。Retrofit, Picasso 和 OkHttp 这三个工具由同一家公司开发,所以他们能完美地补足彼此。 OkHttp can also be used in connection with Volley.

RxJava 是一个响应式编程框架,换句话说,处理异步事件。 它是一种强大并有前途的范例,可能从可读性上讲不是那么理想因为它是如此的不同。我们推荐你在使用这个库来构筑整个应用之前抱持着足够的警惕。有一些项目通过使用 RxJava 构筑, 如果你需要帮助可以和他们之中的人交谈: Timo Tuominen, Olli Salonen, Andre Medeiros, Mark Voit, Antti Lammi, Vera Izrailit, Juha Ristolainen. 我们写了一些博文发表在这上面: [1], [2], [3], [4].

如果你从前没有运用 Rx 的经历,只需要从将它作为对 API 的回应开始即可。你也可以选择从作为简单 UI 事件处理开始,像是 search field 上的点击或者输入事件。如果你对自己的 Rx 技能足够自信并决定将它应用到整个应用构筑中,一定要针对所有不易理解的部分写Javadoc。用心记住其他不熟悉 Rx 的程序员可能会对维护项目感到无比头大。尽你的全力来帮助他们理解你的代码还有 Rx 。

Retrolambda 是一个用来让 JDK8 之前的 Android 或是其他平台支持Lambda表达式语法的库。它用于保持你的代码紧凑并可读,特别是当你使用函数式风格编写代码比如说 RxJava 。为了使用它, 你需要安装 JDK8, 在 Android Studio 的 Project Structure dialog 设置它作为你的 SDK Location , 然后设置环境变量 JAVA8_HOMEJAVA7_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 提供了对于 Java8 lambda 的代码协助支持,如果你是 lambda 的新手,只需从以下建议中开始:

  • 任何只有一个方法的接口都是 "lambda friendly" 的,亦即能被收缩成更紧凑的语法格式
  • 如果对 参数 或其他 的什么拿不准,那么就写一个普通的匿名内部类并让 Android Studio 为你将它收缩成 lambda 格式

注意 dex 方法数限制,避免使用过多库Android apps 当被打包成 dex file 时, 有一个固定的引用方法数限制:65536 [1] [2] [3]. 如果你超出了这一限制,就会在编译时遇见一个致命错误。出于这个理由,使用尽可能少的库,并且使用这个工具 dex-method-counts 来决定使用哪些库集合来保证低于这一限制。特别要避免使用 Guava library, 因为它包含超过 13k 方法.

IDE集成开发环境和文本编辑器

无论使用什么编辑器,一定要构建一个良好的工程结构 编辑器每个人都有自己的
选择,让你的编辑器根据工程结构和构建系统运作,那是你自己的责任。

当下首推Android Studio,因为他是由谷歌开发,最接近Gradle,默认使用最新的工程结构,已经到beta阶段
(目前已经有release 1.0了),它就是为Android开发定制的。

你也可以使用Eclipse ADT ,但是你需要对它进行配置,因为它使用了旧的工程结构
和Ant作为构建系统。你甚至可以使用纯文版编辑器如Vim,Sublime Text,或者Emacs。如果那样的话,你需要使用Gardle和adb命令行。如果使用Eclipse集成Gradle
不适合你,你只是使用命令行构建工程,或迁移到Android Studio中来吧。

无论你使用何种开发工具,只要确保Gradle和新的项目结构保持官方的方式构建应用程序,避免你的编辑器配置文件加入到版本控制。例如,避免加入Ant build.xml文件。
特别如果你改变Ant的配置,不要忘记保持build.gradle是最新和起作用的。同时,善待其他开发者,不要强制改变他们的开发工具和偏好。

Activities and Fragments

针对怎样最佳地通过 Fragments 和 Activities 来组织 Android 架构尚无统一结论,这一点不论在社区还是在 Futurice 的开发者中都是一样。Square 甚至开发了一个库用来最大化地通过View来构筑应用架构 a library for building architectures mostly with Views,以此绕过对于 Fragment 的依赖,但这在社区中仍未被作为广泛推荐的方案。

出于Android API的历史,你能自然地想到将Fragments作为屏幕上的UI碎片。换句话说,Fragments通常与UI相关联。Activities能被自然地想到作为控制器,从生命周期和状态管理上的重要性来说。然而,你很可能遇见角色产生变化的情况:activities可能被作为UI角色(delivering transitions between screens),而fragments能被单独作为控制器 fragments might be used solely as controllers。我们推荐谨慎启航,获知尽可能多的消息然后作出决定,因为无论是选择fragments-only、activities-only还是views-only架构,都存在着其缺陷。这里对于需要小心些什么有一些建议,但你需要持保留态度吸收它们:

  • 避免广泛使用嵌套fragments nested fragments , 这可能会发生 matryoshka bugs 。 要么在有意义的时候使用嵌套fragments (举个例子, 在一个screen-like 的fragment中需要一些 fragments 放在一个水平方向滑动的 ViewPager 中) ,要么就确保这是一个深思熟虑后的决定。
  • 避免放太多代码在activities中。任何情况下只要可能,让它们作为轻量containers,其存在意义首要在于应用的生命周期循环以及其他重要的Android-interfacing APIs。采用单fragment的activity而不是一个单纯的activity,这样可以将UI代码放在fragment中。当你需要改变它以重新放置到一个标签布局或是一个多fragment表格屏幕中去的时候,这使得它能够被复用。避免持有一个无对应fragment的activity,除非你完全知晓这样做的后果。
  • 不要让你的应用的内部工作滥用Android-level APIs,像是重度依赖于Intent。这可能会影响到Android OS或是其他应用,制造bugs或者延迟。举个例子,如果你的应用使用Intent作为内部通信手段,可能会招致多秒延迟——如果它在OS启动后紧接着被用户打开的话。

类库

Jackson 是一个将java对象转换成JSON与JSON转化java类的类库。Gson
是解决这个问题的流行方案,然而我们发现Jackson更高效,因为它支持替代的方法处理JSON:流、内存树模型,和传统JSON-POJO数据绑定。不过,请记住,
Jsonkson库比起GSON更大,所以根据你的情况选择,你可能选择GSON来避免APP 65k个方法限制。其它选择: Json-smart and Boon JSON

网络请求,缓存,图片 执行请求后端服务器,有几种交互的解决方案,你应该考虑实现你自己的网络客户端。使用 Volley
或Retrofit。Volley 同时提供图片缓存类。若果你选择使用Retrofit,那么考虑使用Picasso
来加载图片和缓存,同时使用OkHttp作为高效的网络请求。Retrofit,Picasso和OkHttp都是有同一家公司开发(注:
是由Square 公司开发),所以它们能很好的在一起运行。OkHttp 同样可以和Volley在一起使用 Volley.

RxJava 是函数式反应性的一个类库,换句话说,能处理异步的事件。
这是一个强大的和有前途的模式,同时也可能会造成混淆,因为它是如此的不同。
我们建议在使用这个库架构整个应用程序之前要谨慎考虑。
有一些项目是使用RxJava完成的,如果你需要帮助可以跟这些人取得联系:
Timo Tuominen, Olli Salonen, Andre Medeiros, Mark Voit, Antti Lammi, Vera Izrailit, Juha Ristolainen.
我们也写了一些博客:
[1], [2],
[3],
[4].

如若你之前有使用过Rx的经历,开始从API响应应用它。
另外,从简单的UI事件处理开始运用,如单击事件或在搜索栏输入事件。
若对你的Rx技术有信心,同时想要将它应用到你的整体架构中,那么请在复杂的部分写好Javadocs文档。
请记住其他不熟悉RxJava的开发人员,可能会非常难理解整个项目。
尽你的的全力帮助他们理解你的代码和Rx。

Retrolambda 是一个在Android和预JDK8平台上的使用Lambda表达式语法的Java类库。
它有助于保持你代码的紧凑性和可读性,特别当你使用如RxJava函数风格编程时。
使用它时先安装JDK8,在Android Studio工程结构对话框中把它设置成为SDK路径,同时设置JAVA8_HOMEJAVA7_HOME环境变量,
然后在工程根目录下配置 build.gradle:

dependencies {
    classpath 'me.tatarka:gradle-retrolambda:2.4.+'
}

同时在每个module 的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 提供Java8 lambdas表带是代码提示支持。如果你对lambdas不熟悉,只需参照以下开始学习吧:

  • 任何只包含一个接口的方法都是"lambda friendly"同时代码可以被折叠成更紧凑的语法
  • 如果对参数或类似有疑问,就写一个普通的匿名内部类,然后让Android Status为你生成一个lambda。

当心dex方法数限制,同时避免使用过多的类库 Android apps,当打包成一个dex文件时,有一个65535个应用方法强硬限制[1] [2] [3]。
当你突破65k限制之后你会看到一个致命错误。因此,使用一个正常范围的类库文件,同时使用dex-method-counts
工具来决定哪些类库可以再65k限制之下使用,特别的避免使用Guava类库,因为它包含超过13k个方法。

Java packages architecture Java分包架构

在Java分包架构方面,Android只能算是粗略接近MVC模型Model-View-Controller。在Android中,Fragment和Activity是实际上的控制器类Fragment and Activity are actually controller classes。从另一方面来说,它们又明显是用户接口的部分,所以同时也是视图。

出于这一理由,无法将fragments (or activities)严格划分为控制器或是视图。让它们保持自己的fragments package更好一些。Activities能放在最高级package中只要你遵循之前部分的建议。如果你计划超过两个或三个Activities,那么再加一个 activities package。

另外,也可以像经典的MVC那样来进行分包架构,通过使用一个models package包含POJOs(这些POJOs由JSON解析器解析API responses转化生成),和一个views package包含你的自定义Views,notifications, action bar views, widgets, etc。Adapters算是一个麻烦,存在于数据和视图之间。然而,典型情况是它们会通过getView()方法输出一些视图,因此你可以把adapters subpackage将在views里面。

一些application-wide的和接近于Android系统的控制器类可以放置在一个managers package中。混杂的数据处理类,像是"DateUtils",放在utils package中。那些用于与后端交互的类则放在network package中。

总的来说,序列是从 closest-to-backend 到 closest-to-the-user:

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

Activities and Fragments

Fragments应该作为你实现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 或者 greenrobot EventBus作为更简洁的实现。
    如果你希望避免添加另外一个类库,RxJava同样可以实现一个Event Bus。

  • Fragments 一般通用的不只有UI 你可以有一个没有界面的fragment作为Activity提供后台工作。
    进一步你可以使用这个特性来创建一个fragment 包含改变其它fragment的逻辑
    而不是把这个逻辑放在activity中。

  • 甚至ActionBar 都可以使用内部fragment来管理 你可以选择使用一个没有UI界面的fragment来专门管理ActionBar,或者你可以选择使用在每个Fragment中
    添加它自己的action 来作为父Activity的ActionBar.参考.

很不幸,我们不建议广泛的使用嵌套的fragments,因为
有时会引起matryoshka bugs。我们只有当它有意义(例如,在水平滑动的ViewPager在
像屏幕一样fragment中)或者他的确是一个明智的选择的时候才广泛的使用fragment。

在一个架构级别,你的APP应该有一个顶级的activity来包含绝大部分业务相关的fragment。你也可能还有一些辅助的activity ,这些辅助的activity与主activity
通信很简单限制在这两种方法
Intent.setData()) 或 Intent.setAction())或类似的方法。

Resources 资源

命名 遵循类型前缀惯例,像是 type_foo_bar.xml. Examples: fragment_contact_details.xml, view_primary_button.xml, activity_main.xml.

组织 layout XMLs. 如果你不确定如何格式化 layout XML, 以下惯例会有所帮助:

  • 一个属性一行,4 空格缩进
  • android:id 总是作为第一个属性
  • android:layout_**** 这类属性放在最上面
  • style 属性放在最下面
  • Tag closer /> 拥有自己的一行, 以使顺序清晰和添加属性变得容易
  • 与其使用硬编码 android:text, 不如考虑使用设计时属性 Designtime attributes ,其受 Android Studio支持.
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:andro xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android: 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中。这条法则会有例外,但总体而言工作得很好。这个想法是为了仅将layout (positioning, margin, sizing)和content属性放在layout files中,而外观详情 (colors, padding, font) 放在 styles files中。

那些例外是:

  • android:id 明显应该放在 layout files 中
  • android:orientation 属性对于 LinearLayout 来说一般 放在 layout files 中更有意义
  • android:text 应该放在 layout files 中因为它定义了 content
  • 有些情况下让 style 来定义 android:layout_widthandroid:layout_height 常量会很有用,但默认情况下它们应该出现在 layout files 中

使用 styles. 几乎每一个项目都需要适当地使用 style,因为对于 view 来说有着重复的外观是非常常见的事,看下面这个例子:

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

该 style 被用于 TextViews:

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

你很可能需要为buttons做一些相同的事,不要在这里停下。从宏观角度上提炼出一组相关联的、重复的android:****属性到一个公共的 style 中去。

把一个大的 style 文件分割成多个你无须拘泥于单个 styles.xml 文件。 Android SDK 支持其他不符合这一命名规则的文件,关于文件名 styles什么魔法也没有,起效果的是文件中的 XML tags <style> 。因此你能拥有这样命名的style文件 styles.xml, styles_home.xml, styles_item_details.xml, styles_forms.xml。 不像 resource 目录那样命名对构建系统具有意义, res/values 目录下的文件命名完全可以随意。注:是的,你可以在strings.xml中放color资源,ResourceManager通过映射可以找到它。

colors.xml 是一个颜色调色板 你的colors.xml中不要放其他事物,只需要映射颜色名到一个RGBA值。不要为不同类型的buttons定义RGBA值。

Don't do this:

<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>

你只是简单地采用重复RGBA值来格式化,但这会使得在需要改变基础颜色的时候变得操作复杂。同时,这些定义与上下文紧密关联,像是"button" or "comment",它们应该放置于一个button style 中,而非colors.xml

Instead, do this:

<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>

向应用设计者询问调色板。命名无须全都是颜色名像是"green", "blue", etc.这样的命名也是完全可以接受的:"brand_primary", "brand_secondary", "brand_negative"。像这样格式化颜色会让改变和重定义颜色变得容易,还能让人看出一共有多少种不同的颜色被使用。通常对漂亮的UI设计来说,减少所使用颜色的多样性是一件重要的事。

好好对待 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>

像通常对待strings那样,你应该使用spacing_**** dimensions 来设置 layouting, margins 和 paddings,而不是使用硬编码值。这会带来一致的观感,同时让组织和改变styles及layouts变得简单。

strings.xml

使用类似的命名空间来命名你的strings的keys,不要害怕在两个或多个keys中重复某一个值。语言是很复杂的,所以命名空间是有必要的,它能用于提供上下文信息还有打破模糊。

Bad

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

Good

<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>

不要写全大写的string值。遵循一般的文本惯例(e.g., capitalize first character)。如果你需要将整句string大写显示,那么针对实例使用TextView中的这个属性textAllCaps

Bad

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

Good

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

避免深层级的 views. 有时候你只是想要再加一个LinearLayout,用于完成一些views的布置。但这种情况却可能会发生:

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

尽管你没有在layout文件中直接目击到这样的景象,但这最终有可能发生,如果你填充 views到其他views中。

一些问题可能会发生。你也许遇见过性能问题,因为这样会生成一棵复杂的UI树来让处理器解析。另一个更严重的问题则是可能带来栈溢出错误: StackOverflowError.

因此,试着让你的views层级尽可能的扁平:学习如何使用RelativeLayout, 如何优化你的布局 optimize your layouts 还有如何使用 <merge> tag.

清楚与 WebView 相关的问题 当你必须要显示一个web页面的时候,比如说一篇文章,避免客户端侧的对于HTML的清理处理,更好的方式是从后端程序中直接获取一段 "纯粹的" HTML 。当持有Activity的引用而非ApplicationContext时,WebView还可能导致内存泄漏WebViews can also leak memory when they keep a reference to their Activity, instead of being bound to the ApplicationContext。避免用 WebView 来做一些简单的文本或按钮, 更好的选择是 TextViews 或 Buttons。

Java 包结构

Android 应用程序在架构上大致是Java中的Model-View-Controller结构。
在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

Test frameworks 测试框架

Android SDK's testing framework尚不完善,特别是有关于UI 测试。Android Gradle实现了一个命名为connectedAndroidTest的测试任务,它能运行你创造的JUnit test,参考extension of JUnit with helpers for Android.这意味着你需要连接实机或模拟器来运行测试,参考官方测试的指南[1] [2]

使用 Robolectric 作为 unit tests, 而不要做 views tests 这是一个致力于提高开发速度的无须连接设备的测试框架,特别适用于针对models 和 view models的单元测试。然而,Robolectric对于UI tests是不完全并且错误的。在测试以下相关UI元素时会有问题:animations, dialogs, etc,而且当你“行走于黑暗中”(没法看到屏幕正被控制着操作)时,实际情况究竟怎样也是非常难以理解的。

Robotium 让写 UI tests 变得简单 你可能不需要用Robotium来连接实机跑UI case,但你仍会通过它获取好处,因为它提供了许多helpers用于获取及分析views以及控制屏幕。Test cases 将看起来很简单像是这样:

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

资源文件 Resources

  • 命名 遵循前缀表明类型的习惯,形如type_foo_bar.xml。例如:fragment_contact_details.xml,view_primary_button.xml,activity_main.xml.

组织布局文件 若果你不确定如何排版一个布局文件,遵循一下规则可能会有帮助。

  • 每一个属性一行,缩进4个空格
  • android:id 总是作为第一个属性
  • android:layout_**** 属性在上边
  • style 属性在底部
  • 关闭标签/>单独起一行,有助于调整和添加新的属性
  • 考虑使用Designtime attributes 设计时布局属性,Android Studio已经提供支持,而不是硬编码android:text
    (译者注:墙内也可以参考stormzhang的这篇博客链接)。
<?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:**** 应放在 styler XML中。此规则也有例外,不过大体工作
的很好。这个思想整体是保持layout属性(positioning, margin, sizing) 和content属性在布局文件中,同时将所有的外观细节属性(colors, padding, font)放
在style文件中。

例外有以下这些:

  • android:id 明显应该在layout文件中
  • layout文件中android:orientation对于一个LinearLayout布局通常更有意义
  • android:text 由于是定义内容,应该放在layout文件中
  • 有时候将android:layout_widthandroid:layout_height属性放到一个style中作为一个通用的风格中更有意义,但是默认情况下这些应该放到layout文件中。

使用styles 几乎每个项目都需要适当的使用style文件,因为对于一个视图来说有一个重复的外观是很常见的。
在应用中对于大多数文本内容,最起码你应该有一个通用的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中。

将一个大的style文件分割成多个文件 你可以有多个styles.xml 文件。Android SDK支持其它文件,styles这个文件名称并没有作用,起作用的是在文件
里xml的<style>标签。因此你可以有多个style文件styles.xml,style_home.xml,style_item_details.xml,styles_forms.xml
不用于资源文件路径需要为系统构建起的有意义,在res/values目录下的文件可以任意命名。

colors.xml是一个调色板 在你的colors.xml文件中应该只是映射颜色的名称一个RGBA值,而没有其它的。不要使用它为不同的按钮来定义RGBA值。

不要这样做

<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>

使用这种格式,你会非常容易的开始重复定义RGBA值,这使如果需要改变基本色变的很复杂。同时,这些定义是跟一些环境关联起来的,如button或者comment,
应该放到一个按钮风格中,而不是在color.xml文件中。

相反,这样做:

<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>

向应用设计者那里要这个调色板,名称不需要跟"green", "blue", 等等相同。
"brand_primary", "brand_secondary", "brand_negative" 这样的名字也是完全可以接受的。
像这样规范的颜色很容易修改或重构,会使应用一共使用了多少种不同的颜色变得非常清晰。
通常一个具有审美价值的UI来说,减少使用颜色的种类是非常重要的。

像对待colors.xml一样对待dimens.xml文件 与定义颜色调色板一样,你同时也应该定义一个空隙间隔和字体大小的“调色板”。
一个好的例子,如下所示:

<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>

布局时在写 margins 和 paddings 时,你应该使用spacing_****尺寸格式来布局,而不是像对待String字符串一样直接写值。
这样写会非常有感觉,会使组织和改变风格或布局是非常容易。

避免深层次的视图结构 有时候为了摆放一个视图,你可能尝试添加另一个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文件中从一个view inflate(这个inflate翻译不过去,大家理解就行) 到其他views当中,也是可能会发生的。

可能会导致一系列的问题。你可能会遇到性能问题,因为处理起需要处理一个复杂的UI树结构。
还可能会导致以下更严重的问题StackOverflowError.

因此尽量保持你的视图tree:学习如何使用RelativeLayout,
如何 optimize 你的布局 和如何使用
<merge> 标签.

小心关于WebViews的问题. 如果你必须显示一个web视图,
比如说对于一个新闻文章,避免做客户端处理HTML的工作,
最好让后端工程师协助,让他返回一个 "" HTML。
WebViews 也能导致内存泄露
当保持引他们的Activity,而不是被绑定到ApplicationContext中的时候。
当使用简单的文字或按钮时,避免使用WebView,这时使用TextView或Buttons更好。

Emulators 模拟器

如果你是专业的Android apps开发者,买一个专业版 Genymotion emulator吧。Genymotion比原生模拟器运行起来有着更高的帧速。它拥有一些工具用于调试你的应用,像是模拟网络连接质量,GPS位置等。用于连接着进行UI test它也很理想。你还能获取许多不同的虚拟设备,与购买许多实机相比Genymotion专业版的花费实在是十分便宜。

警告:Genymotion emulators不支持所有的Google服务像是Google Play Store and Maps.要是你想要测试三星特征的APIs,仍旧有必要拥有一台三星实机。

测试框架

Android SDK的测试框架还处于初级阶段,特别是关于UI测试方面。Android Gradle
目前实现了一个叫connectedAndroidTest的测试,
它使用一个JUnit 为Android提供的扩展插件 extension of JUnit with helpers for Android.可以跑你生成的JUnit测试,

只当做单元测试时使用 Robolectric ,views 不用
它是一个最求提供"不连接设备的"为了加速开发的测试,
非常时候做 models 和 view models 的单元测试。
然而,使用Robolectric测试时不精确的,也不完全对UI测试。
当你对有关动画的UI元素、对话框等,测试时会有问题,
这主要是因为你是在 “在黑暗中工作”(在没有可控的界面情况下测试)

**Robotium 使写UI测试非常简单。
** 对于UI测试你不需 Robotium 跑与设备连接的测试。
但它可能会对你有益,是因为它有许多来帮助类的获得和分析视图,控制屏幕。
测试用例看起来像这样简单:

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

Proguard configuration Proguard配置

ProGuard 常作为Android项目中缩减体积、混淆代码的工具。

是否使用ProGuard取决于你的项目配置。通常你可以在gradle中像这样配置以在构建正式apk时使用ProGuard。

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

为了决定某些代码是需要保持原样还是丢弃(注:即未实际使用,不打包,这也就是为什么使用ProGuard会减少应用体积)还是混淆,你需要在代码中指出一个或多个关键点。这些关键点区分出这些典型的类:with main methods, applets, midlets, activities, etc.Android framework使用的默认混淆配置能在这里找到:SDK_HOME/tools/proguard/proguard-android.txt,使用这个配置,再加上你自己在这里定义的项目限定的配置:my-project/app/proguard-rules.pro,将会共同构成最终的ProGuard混淆规则。

使用ProGuard经常遇见的一个问题是应用启动时闪退,错误信息则为ClassNotFoundException or NoSuchFieldException or similar,尽管运行构建命令成功 (i.e. assembleRelease) 且无警告。这意味着以下一到两件事:

  1. ProGuard移除了类、枚举、方法、域或注解,检查一下哪些是不需要移除的部分。
  2. ProGuard混淆了类、枚举、域,但这些都在代码中被直接使用了它们原本的命名, i.e. through Java reflection.

检查app/build/outputs/proguard/release/usage.txt看是否有存疑对象被移除掉了。检查 app/build/outputs/proguard/release/mapping.txt 看是否有存疑对象被混淆了。

防止ProGuard丢弃一些需要的类或类成员,在你的ProGuard配置中加入keep options:

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

防止ProGuard混淆一些需要的类或类成员,添加keepnames:

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

Check this template's ProGuard config for some examples.Read more at Proguard for examples.

尽早地在你的项目中提供一个正式版本构建 用来检查ProGuard是否执行正确符合预期是十分重要的事。当你引用了新库的时候,记得构建一个正式 版本在实机上测试一下。不要等到你的应用要发布"1.0"版本了再来构建正式版本,你可能会遭遇一些令人不愉快的惊喜,而你没有剩下的时间去修正它们。

建议 保存好每一个你发布给你用户的正式版本的mapping.txt file 。通过持有这些文件,你才能够debug一些问题,当你的用户遇见bug并提交了一份带有混淆的stack trace.

DexGuard. 如要你需要一个 hard-core tools 来优化并混淆正式版本代码, 可以考虑 DexGuard, 制作 ProGuard 的团队推出的商用软件. 它还能轻松地分割 Dex files 以解决65k方法数限制.

模拟器

如果你全职开发Android App,那么买一个Genymotion emulatorlicense吧。
Genymotion 模拟器运行更快的秒帧的速度,比起典型的AVD模拟器。他有演示你APP的工具,高质量的模拟网络连接,GPS位置,等等。它同时还有理想的连接测试。
你若涉及适配使用很多不同的设备,买一个Genymotion 版权是比你买很多真设备便宜多的。

注意:Genymotion模拟器没有装载所有的Google服务,如Google Play Store和Maps。你也可能需
要测试Samsung指定的API,若这样的话你还是需要购买一个真实的Samsung设备。

Data storage 数据存储

如果你只需持久化简单的标志位并且你的应用只在单进程环境下运行。SharedPreferences对你来说很可能已经足够了。它是不错的默认选项。

这儿有两个原因会让你不想要使用SharedPreferences:

  • 性能: 你拥有大量数据或者数据本身非常复杂
  • 多进程获取数据: 你拥有运行在各自进程中的组件或是远程服务,它们需要同步数据

在SharedPreferences不够满足你的需求的情况下,你应该使用作为平台标准的ContentProvider,它快速并且进程安全。

关于ContentProvider的问题则在于你在使用它之前需要写大量的样板似的代码,还有就是低质量的学习指南。如果可能的话,使用自动库来生成ContentProvider,这样会显著减少劳力。such as Schematic.

你仍然需要靠你自己来写一些解析代码用于从Sqlite列中读取出Object数据,反之亦然。你可以序列化数据对象,像是使用Gson,并且只持有结果字串。通过这种方式你会损失一些性能,但另一方面你将不需要为数据类中的每一个域都声明列。

我们通常不推荐使用对象关系映射库Object-Relation Mapping library,除非你有着不寻常的复杂数据和迫切的需要。它们趋向复杂并需要时间去学习。如果你决定使用ORM了,要是你的应用对 进程安全 process safe 有需求的话就要注意所使用的库是否支持 这一特性,许多现存的ORM解决方案令人惊讶地不支持。

混淆配置

ProGuard 是一个在Android项目中广泛使用的压缩和混淆打包的源码的工具。

你是否使用ProGuard取决你项目的配置,当你构建一个release版本的apk时,通常你应该配置gradle文件。

buildTypes {
    debug {
        minifyEnabled false
    }
    release {
        signingConfig signingConfigs.release
        minifyEnabled true
        proguardFiles 'proguard-rules.pro'
    }
}

为了决定哪些代码应该被保留,哪些代码应该被混淆,你不得不指定一个或多个实体类在你的代码中。
这些实体应该是指定的类包含main方法,applets,midlets,activities,等等。
Android framework 使用一个默认的配置文件,可以在SDK_HOME/tools/proguard/proguard-android.txt
目录下找到。自定义的工程指定的 project-specific 混淆规则,如在my-project/app/proguard-rules.pro中定义,
会被添加到默认的配置中。

关于 ProGuard 一个普遍的问题,是看应用程序是否崩溃并报ClassNotFoundException 或者 NoSuchFieldException 或类似的异常,
即使编译是没有警告并运行成功。
这意味着以下两种可能:

  1. ProGuard 已经移除了类,枚举,方法,成员变量或注解,考虑是否是必要的。
  2. ProGuard 混淆了类,枚举,成员变量的名称,但是这些名字又被拿原始名称使用了,比如通过Java的反射。

检查app/build/outputs/proguard/release/usage.txt文件看有问题的对象是否被移除了。
检查 app/build/outputs/proguard/release/mapping.txt 文件看有问题的对象是否被混淆了。

In order to prevent ProGuard from stripping away needed classes or class members, add a keep options to your proguard config:
以防 ProGuard 剥离 需要的类和类成员,添加一个 keep选项在你的 proguard 配置文件中:

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

防止 ProGuard 混淆 一些类和成员,添加 keepnames:

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

查看this template's ProGuard config 中的一些例子。
更多例子请参考Proguard。

在构建项目之初,发布一个版本 来检查ProGuard规则是否正确的保持了重要的部分。
同时无论何时你添加了新的类库,做一个发布版本,同时apk在设备上跑起来测试一下。
不要等到你的app要发布 "1.0"版本了才做版本发布,那时候你可能会碰到好多意想不到的异常,需要一些时间去修复他们。

Tips每次发布新版本都要写 mapping.txt。每发布一个版本,如果用户遇到一个bug,同时提交了一个混淆过的堆栈跟踪。
通过保留mapping.txt文件,来确定你可以调试的问题。

DexGuard 若果你需要核心工具来优化,和专门混淆的发布代码,考虑使用DexGuard,
一个商业软件,ProGuard 也是有他们团队开发的。
它会很容易将Dex文件分割,来解决65K个方法限制问题。

Use Stetho

Stetho,一款来自于Facebook 的Android applications debug bridge,与Chrome 开发者工具集成在一起。使用它你很轻易就能检查应用,尤其是网络通信。它还能让你简单地检查和编辑SQLite 数据库、shared preferences。但是,你应用确保Stetho 仅在debug版本中可用,release版本中不可用。

Thanks to

Antti Lammi, Joni Karppinen, Peter Tackage, Timo Tuominen, Vera Izrailit, Vihtori Mäntylä, Mark Voit, Andre Medeiros, Paul Houghton and other Futurice developers for sharing their knowledge on Android development.

License

Futurice OyCreative Commons Attribution 4.0 International (CC BY 4.0)

译者推荐阅读

  • dex分包
  • 「打造自己的Library」SharedPreferences篇
  • 使用Chrome来调试你的Android App

本文由今晚最快开奖现场直播发布于编程应用,转载请注明出处:支付最好实施,Android最棒实践指南

关键词:

swift中自定义Log,iOS开发之自定义log

在我们日常开发中,打印输出作为调试手段还是占据相当大的一个比重的.但是打印又非常消耗性能,我们仅仅希望在调...

详细>>

浅析饿了么,仿抖音下拉刷新

难度:⭐️⭐️效果: 话不多说,先上DEMO记得star哦 下拉刷新 下拉进入活动会场 效果图 饿了么App在最近版本上线了...

详细>>

Masonry简单使用,Masonry介绍与使用实践

1)Masonry是一个轻量级的布局框架,拥有自己的描述语法,采用更优雅的 链式语法 封装自动布局,简洁明了并具有...

详细>>

增加和删除改查

一.文章概要 当前使用工具是XCode7. 这篇文章主要是写了对于基本数据类型的"增删改查"的操作,至于特殊类型比如UII...

详细>>