【安卓开发】最佳实践之一:安卓开发篇

这篇文章主要为Futurice公司Android开发者总结的经验教训。遵循这些规范能够避免无谓的重复劳动。若是对iOS或Windows Phone平台的开发感兴趣,请查看iOS最佳实践文档Windows客户端最佳实践文档html

欢迎反馈,但请先阅读反馈规范java

摘要

  • 使用Gradle和Gradle默认的项目结构
  • 将密码和敏感数据放在gradle.properties中
  • 不要实现本身的HTTP客户端,使用Volley或者OkHttp库
  • 使用Jackson库解析JSON数据
  • 因为65K的方法空间限制,避免使用Guava并使用尽量少的库
  • 用Fragment来显示UI
  • Activity只用来管理Fragment
  • XML也是代码,管理好XML代码
  • 使用样式来减小布局XML代码中重复属性
  • 将样式写在多个文件中,避免把样式所有写在单一的大文件当中
  • 保持colors.xml文件的简短干净,只定义调色板
  • 一样也保持dimens.xml简短干净,只定义通用的常量
  • 避免深层级的ViewGroup
  • 避免客户端处理WebView要显示的内容,而且注意内存泄露
  • 使用Robolectric进行单元测试,使用Robotium进行链接设备(UI)的测试
  • 使用Genymotion模拟器
  • 一直使用ProGuard或者DexGuard

Android SDK

Android SDK存放在home目录或者其余跟应用开发无关的位置。一些IDE在安装时包含了SDK,这时SDK可能存放在IDE的安装目录下。而这是很很差的作法,特别是当你须要升级(或者从新安装)或更换IDE时。同时也要避免把SDK存放在系统目录下,不然,当普通用户(不是root)使用IDE时就须要获取sudo权限。android

编译系统

编译系统首选Gradle。相比于Gradle,Ant更加的局限而且更加繁琐。使用Gradle编译系统能够很简单的作到:ios

  • 将应用编译成不一样的版本
  • 完成简单的相似脚本的任务
  • 管理和下载依赖
  • 自定义秘钥仓库
  • 其余…

Google正积极的开发安卓Gradle插件,做为新的标准编译系统。git

项目结构

主要有两个主流的项目结构:旧的Ant项目结构和Eclipse ADT项目结构,较新的Gradle和Android Studio项目结构。固然选择新的项目结构。若是你的项目正在用旧的项目结构,考虑放弃旧的结构,转移到新的项目结构下吧。程序员

旧项目结构:github

1
2
3
4
5
6
7
8
9
10
old-structure
├─ assets
├─ libs
├─ res
├─ src
│  └─ com /futurice/project
├─ AndroidManifest.xml
├─ build.gradle
├─ project.properties
└─ proguard-rules.pro

 

新的项目结构:web

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

新旧项目结构最大的不一样点是新项目结构更加合理的分开了代码集(main, androidTest)。例如,你能够在代码集src文件夹下添加’paid’和’free’文件夹,分别用于存放付费版应用代码和免费版应用的代码。shell

顶层app文件夹用于将你的应用和其余库(例如:library-foobar)区分开来。Settings.gradle中保存了app/build.gradle须要用到的库的引用。编程

Gradle配置

普通项目结构。遵循Google安卓Gradle规范
简单任务。能够用Gradle完成一些简单任务,而不用特意去写(shell, Python, Perl等)脚本。具体参考Gradle文档
密码。你须要在build.gradle中配置应用发行版本的签名配置。如下这些状况是须要避免的:

不要这样作。也许你会在版本控制系统中这样作。

1
2
3
4
5
6
7
8
signingConfigs {
     release {
         storeFile file ( "myapp.keystore" )
         storePassword "password123"
         keyAlias "thekey"
         keyPassword "password789"
     }
}

换一种方式,新建一个gradle.properties文件,文件内容以下。注意,不要把Gradle.properties添加到版本控制系统中。

KEYSTORE_PASSWORD=password123
KEY_PASSWORD=password789

Gradle会自动导入gradle.properties文件,因此你能够在build.gradle中这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
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文件到项目中,那这些依赖的jar文件只会是某个固定的版本,例如2.1.1。下载jar文件并管理更新这种方式笨拙不堪,而Maven彻底解决了这个问题,而且,Maven能够集成在安卓Gradle编译系统中。你能够指定版本的范围,例如2.2.+,而后Maven就会自动更新到版本范围内的最新版本。例如:

1
2
3
4
5
6
7
8
9
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.+'
}

IDE和文本编辑器

无论用什么编辑器,它都必需要可以很好的显示项目结构。编译器的选择看我的喜爱,可是编辑器必需要可以显示项目结构和编译。

如今最为推荐的IDE时Android Studio,由于Android Studio由Google开发,最为接近Gradle,默认使用新的项目结构,也终于发布了beta版,能够说是为Android开发量身定作的IDE。

固然你也可使用Eclipse ADT,可是须要从新配置,由于Ecplise ADT默认使用旧的项目结构和使用Ant编译。甚至,可使用纯文本编辑器,好比Vim, Sublime Text, 或者Emacs。若是使用纯文本编辑器,就须要在命令行中使用Gradle和adb。若是Eclipse集成Gradle后仍旧不能工做,你能够选择在命令行中编译,或者迁移至Android Studio。

无论使用什么IDE和文本编辑器,确保使用Gradle和新的项目结构来编译应用程序,同时避免把编译器的配置文件添加到版本控制系统当中。例如,避免添加Ant的配置文件build.xml。还有须要强调的一点,若是你在Ant中更改了编译配置,不要忘记更新build.gradle,使其可以完成编译。另外,对其余的开发者友好一点,不要强迫他们去改变他们的工具的偏好设置。

Jackson是一个用于将对象转换成JSON或者将JSON转换成对象的Java库。为了解决JSON和对象相互转换的问题,Gson是一个受欢迎的选择。可是咱们发现,自从Jackson支持多种JSON处理方式:流,内存中的树模型和传统的JSON-POJO数据绑定,Jackson更加高效。请记住,Jackson是一个比GSON大的库,因此请根据你本身的实际状况作出选择。考虑到65K的方法空间限制,你可能会偏向于选择GSON。其余选择:Json-smartBoon JSON

网络,缓存和图片。如今已经有许多通过实践证实的向后端服务器请求数据的解决方案。你应该考虑使用这些解决方案来实现本身的客户端。使用Volley或者Retrofit。Volley也提供了加载和缓存图片的帮助类。若是你选择Retrofit,考虑使用Picasso来加载和缓存图片,使用OkHttp来实现高效的HTTP请求。Retrofit,Picasso和OkHttp都由同一个公司实现,因此这三者契合的特别好。OkHttp也能够和Volley配套使用。

RxJava是一个用于响应式编程的库,也便是,处理异步事件的库。RxJava这个范例很是强大,并且前途光明。RxJava很是不同凡响,所以使用RxJava时可能会使人迷惑。咱们推荐在把RxJava部署到整个应用前先花一些时间了解RxJava。如今已经有一些项目是利用RxJava来完成的,若是你须要帮助,请向这些人询问:Timo Tuominen, Olli Salonen, Andre Medeiros, Mark Voit, Antti Lammi, Vera Izrailit, Juha Ristolainen。另外,咱们也写了一些博客:[1][2][3][4].

若是你没有使用Rx的经验,请从应用Rx的响应API开始。或者,从应用Rx的UI事件处理开始,好比点击事件或者在搜索框中的键盘事件。若是你对使用Rx颇有信心,想要把Rx应用到整个应用程序当中,请在比较难处理、容易使人迷惑的部分写明Javadocs。记住,其余不熟悉RxJava的程序员维护项目时可能会很是困难。请尽力去帮助他去理解你的代码和Rx。

Retrolambda是一个在Android平台或者其余低于JDK8的平台上处理Lambda表达式语法的Java库。利用这个库,能够保持你的代码的整洁严谨而且具备可读性,特别是当你使用了函数式编程风格(functional style),例如使用了RxJava。使用前,先安装JDK8,在Android Studio项目结构对话框中将它设置为你的SDK路径,设置JAVA8_HOME和JAVA7_HOME环境变量,而后在项目根目录下build.gradle中增长如下内容:

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

而后在每个模块下的build.gradle中,增长如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
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友好」的,可以被转换成更加整洁严谨的语法。
  • 若是你不肯定参数或者其余信息,写一个普通的匿名内部类,而后让Android Studio将它转换成一个lamdba表达式。

请注意dex方法限制,避免使用过多的库。被打包成dex文件的安卓应用,都有一个硬性的限制:最多能有65536个方法引用[1] [2] [3]。若是你超出了这个限制,在编译的时候你就会看到一个严重的编译错误。所以,使用尽可能少的库,并使用dex-method-counts工具来决定在保证不超出限制的前提下,有哪些库可使用。特别要避免使用Guava库,由于它包含了超过13k个方法。

在Android应用开发中,首选Fragment来显示UI。Fragment是可重用的用户交互界面,而且能够将Fragment组合在一块儿。咱们推荐使用Fragment来显示用户交互界面,而不是使用Activity。如下是一些理由:

  •  实现多视图布局。将手机应用扩展至平板的主要方法即是利用Fragment。利用Fragment,可让视图A和B都显示在一个平板屏幕上,而在手机屏幕上,视图A和B都占一整块屏幕。若是你的应用从一开始就用Frament来实现,那么你很容易就能将你的应用适配到屏幕大小不一样的设备上。
  • 屏与屏之间的通讯。 安卓API并无提供一个恰当的方法将复杂的数据(例如,一些Java对象)从一个Activity发送到另一个Activity中。可是利用Fragment时,以activity实例为通讯管道,能够实现该activity下的子fragment之间的通讯。即便这种方法优于Activity之间的通讯,你可能仍旧须要一个事件总线的架构,考虑使用Otto或者greenrobot EventBus
  • Fragment有更好的普适性,而不只仅只是实现UI。你能够实现一个没有UI的fragment,做为activity后台运行的「工人」。你也能够将这个点子发挥的更淋漓尽致一点,好比建立一个fragment专门用于实现fragment的改变逻辑,而不是将这些逻辑写在activity中。
  • 甚至ActionBar也能够在fragment中管理。你能够建立一个没有UI的fragment,只用于管理ActionBar,或者在每个当前可见的fragment中把本身须要的action项添加到父activity的ActionBar上。阅读更多内容

虽然咱们建议使用fragment,可是咱们不建议大量使用嵌套的fragment,由于可能会引发“套娃式bug”(matryoshka bugs)。只在合理的状况下(例如,水平滑动的ViewPager中的fragment嵌套在一个模拟屏幕的fragment中)或者通过深思熟虑时,才使用嵌套的fragment。

从架构层面来说,你的应用应该有一个顶层的activity,其中activity中包含了大部分的业务相关的fragment。你也能够有其余的辅助activity,只要这些activity和主activity的通讯足够简单,可以经过Intent.setData()或者Intent.setAction()或者其余简单的方式实现便可。

Java包结构

Android应用程序的Java包结构能够用基本上近似于模型-视图-控制器结构。对于Android,Fragment和Activity实际上就是控制类。同时,这二者也是用户交互界面的一部分,所以,这二者也是视图。

因为上述缘由,将fragment(或者activity)严格的归类为控制器或者是视图是很是困难,不合理的。因此,更合理的作法是把fragment存放在专有的fragment包内。若是你遵循了前一部分的建议,那么能够将activity存放在最顶层的包下。若是你计划建立多于2个或3个activity,那么建立一个activities包。

不然(译者注:若是没有fragment和activity),包结构看起来就是一个典型的MVC结构。有一个models包,存放主要用于JSON解析时API返回值的POJO对象;一个views包,存放你自定义的视图,通知,action bar视图和小部件等。Adapter的归类比较模糊,是处于数据和视图之间的位置。可是,通常状况下,adapter须要在getView()函数中引入一些视图,因此能够在views包下建一个adapters包来存放adpater。

一些控制类是整个应用程序都须要使用到的,也更加接近安卓系统底层。这些控制类存放在managers包下。各类数据处理类,例如「DateUtils」,存放在utils包下。负责与后端服务器进行交互的类存放在network包下。

总之,按靠近后端服务器到靠近用户的顺序排列,包结构以下:

1
2
3
4
5
6
7
8
9
10
11
com.futurice.project
├─ network
├─ models
├─ managers
├─ utils
├─ fragments
└─ views
    ├─ adapters
    ├─ actionbar
    ├─ widgets
    └─ notifications

 资源

命名。遵循以类型做为前缀的习惯,像type_foo_bar.xml。例如:fragment_contact_details.xml,view_primary_button.xml,activity_main.xml。

管理好布局XML代码。若是你不肯定如何按照必定的格式来管理XML,能够参考如下几个习惯:

  • 一个属性占单独的一行,缩进4个空格
  • android:id老是第一个属性
  • android:layout_****属性放在顶部
  • style属性放在底部
  • 标签关闭/>独占一行,便于调整属性的顺序和增长属性
  • 不要在android:text中硬编码字符串,考虑使用Android Studio中提供的Designtime attributes功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<? 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 >

最重要的规则是,在布局XML中定义android:layout_****属性,而其余的android:****属性则在样式XML中定义。这个规则有例外的状况,可是大部分状况下是适用的。这个规则保证只有layout属性(positioning, margin, sizing)和内容属性在布局文件中,其余的外观属性(colors, padding, font)则定义在样式文件中。

例外的状况有:

  • android:id显然应该在布局文件中定义
  • LinearLayout的android:orientation属性在布局文件中定义更为合理
  • android:text应该在布局文件中定义,由于它定义了特定的内容(译者注:属于内容属性)
  • 有时候建立通用的样式文件来定义android:layout_width和android:layout_height更加合理,可是通常状况下这两个属性应该在布局文件中定义。

使用样式。在项目中,重复的view的外观(译者注:重复的view属性)是很常见的,所以,基本上每一个项目都须要恰当的使用样式。在一个应用程序中,至少应该有一个通用的文本内容的样式。例如:

1
2
3
4
< style name = "ContentText" >
     < item name = "android:textSize" >@dimen/font_normal</ item >
     < item name = "android:textColor" >< a href = "http://www.jobbole.com/members/color/" rel = "nofollow" >@color</ a >/basic_black</ item >
</ style >

应用到TextView当中以下:

1
2
3
4
5
6
< TextView
     android:layout_width = "wrap_content"
     android:layout_height = "wrap_content"
     android:text = "@string/price"
     style = "@style/ContentText"
     />

你也可能须要给button按钮写一个通用的样式, 不过不要只停留在给文本内容和按钮写通用样式上。继续的深刻应用这个思想,把View的相关的重复的属性写成通用的样式。

把大的样式文件分红多个小样式文件。你不必定非得只有一个styles.xml文件。Android SDK支持以非传统方式命名的样式文件。文件名styles并无特别的做用,起做用的只是文件中的XML标签<style>。所以,一个项目中能够同时有这些样式文件styles.xmlstyles_home.xml,styles_item_details.xmlstyles_forms.xml。不像资源目录名那样在编译时有特殊意义,在res/values下的文件名是任意的。

colors.xml是颜色调色板。colors.xml中应该只包含一些颜色名字到RGBA颜色值的映射。不要在colors.xml中为不一样的按钮定义不一样的颜色。

不要像下面这样作:

1
2
3
4
5
6
7
8
9
< 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”这些,应该在按钮的样式文件中定义,而不是在colors.xml中定义。

你能够这样作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
< 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。一样,你能够定义间隔,字体大小等属性的”调色板”,理由和管理颜色的理由同样。下面是dimens文件的一个好样例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
< 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 >

你应该使用(译者注:dimens文件中定义的)spacing_****尺寸来实现视图布局的margin和padding属性,而不是在布局文件中硬编码属性值,这一点很像字符串的通常处理方式。这会使应用保持一致的观感,同时在管理和更改样式和布局时也更加方便。

避免深层级视图。有时候,你想要在原有的视图xml中添加一个新的LinearLayout,以此来实现一个新的视图。那么,颇有可能发生下面的状况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
< LinearLayout
     android:layout_width = "match_parent"
     android:layout_height = "match_parent"
     android:orientation = "vertical"
     >
 
     < RelativeLayout
         ...
         >
 
         < LinearLayout
             ...
             >
 
             < LinearLayout
                 ...
                 >
 
                 < LinearLayout
                     ...
                     >
                 </ LinearLayout >
 
             </ LinearLayout >
 
         </ LinearLayout >
 
     </ RelativeLayout >
 
</ LinearLayout >

即便你没有直接在一个布局文件中看到上述的状况,当你在把一个视图填充(在Java代码中)到另外一个视图中时,上述的状况也有可能发生。

这可能引起一系列的问题。可能会有性能问题,由于在这种状况下,处理器须要处理很是复杂的UI树。另一个更严重的错误是栈溢出错误

所以,尽量的减小视图的层级:学习如何使用RelativeLayout,如何优化布局和如如何使用<merge>标签

谨慎处理与WebView相关的问题。当你必须显示一个网页时,例如一篇新闻,不要在客户端中处理HTML,更好的作法是向后端程序员请求”纯净”的HTML代码。当你把WebView绑定到activity上,而不是绑定到ApplicationContext上时,WebView也可能会泄露内存。不要使用WebView来展示简单文字或者按钮,用TextView和Button来实现。

 测试框架

Android SDK提供的测试框架仍旧不够完善,特别是UI测试。Android Gradle如今利用一个为安卓定制的JUnit帮助工具插件,实现了一个测试框架connectedAndroidTest来执行你建立的JUnit测试。也就是说,在进行测试时,你须要链接设备或者模拟器。请根据官方的测试指南[1] [2]来操做。

只用Robolectric来单元测试,不用于视图UI测试。为了保证开发速度,Robolectric这个测试框架致力于提供不链接设备时的测试,也便是适合于对模型和视图模型的单元测试。可是,在Robolectric的框架下测试UI是不许确,不彻底的。在测试和动画,对话框相关的UI元素时,你可能会遇到一些问题。由此,你”坠入了深渊”(测试过程当中看不到控制屏幕),这使测试变得很是复杂。

Robotium让写UI测试变得很是容易。在Robotium测试框架下测试UI,你不须要进行链接设备的测试,可是利用Robotium提供的大量的帮助工具,你能够很是方便的分析视图UI和控制屏幕。测试用例也很是简单,如下是一个例子:

1
2
3
4
5
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" ));

模拟器

若是你以开发安卓应用为职业,那么买一个正版的Genymotion模拟器吧。相比于AVD模拟器,Genymotion模拟器具备更高的帧率。它提供了一些工具来演示你的应用,模拟网络链接质量,GPS定位等。固然,Genymotion也适合于进行链接设备的测试。(译者注:为了全面的测试)你须要买不少(但不是所有)不一样的设备,所以花钱买一个正版的Genymotion模拟器会比买不少物理设备便宜不少。

注意:Genymotion模拟器不会实现全部的谷歌服务,例如Google Play商店和地图。若是你须要测试三星独有的API,那仍是有必要买一个三星的设备。

Proguard配置

通常状况下,ProGuard用于缩减和混淆安卓项目的打包代码。

是否使用Proguard取决于你的项目配置。大部分状况下,当你编译一个发行版本的apk时,你须要配置gradle来运行ProGuard。

1
2
3
4
5
6
7
8
9
10
buildTypes {
     debug {
         runProguard false
     }
     release {
         signingConfig signingConfigs.release
         runProguard true
         proguardFiles 'proguard-rules.pro'
     }
}

为了判断要保留哪些代码,忽略或者混淆哪些代码,你必须明确的指出一个或者多个代码入口。 这些代码入口通常为包含有main函数的类,Java小程序(applet),移动信息设备小程序(Midlet),activity等。你在SDK_HOME/tools/proguard/proguard-android.txt能够找到安卓框架提供的默认配置。每一个项目在my-project/app/proguard-rules.pro中自定义的proguard规则,(译者注:执行proguard时)会被附加到默认配置上。

有一个跟ProGuard相关的常见问题,在应用程序启动时由于ClassNotFoundException或者NoSuchFieldException或者相似的异常而崩溃,即便你在编译命令行(例如,assmbleRelease)中成功的完成编译而且没有warning提示。不外乎如下两种状况:

  1. ProGuard认为一些类,枚举,方法,变量或者注解不须要,将其移除了。
  2. ProGuard混淆了类,枚举,或者变量,可是这些类可能被经过它原来的名字间接地调用了,例如,经过Java的反射机制调用。

查看app/build/outputs/proguard/release/usage.txt,看形成崩溃问题的对象是否被移除了。查看app/build/outputs/proguard/release/mapping.txt,看形成崩溃问题的对象是否被混淆了。

为了防止ProGuard剔除须要用到的类或者类成员,在你的proguard配置中添加一个keep项:

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

为了防止ProGuard混淆一些类或者类成员,添加一个keepnames项:

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

在这份ProGuard配置模板中有一些例子。在ProGuard文档中有更多的例子。

提示:把每个发行版本的mapping.txt文件都保存下来。这样,当用户遇到一个bug,提交了一个混淆的调用栈时,即可以根据保存的mapping.txt来调试,找到问题所在。

DexGuard。若是你须要一个不错的工具来优化代码,特别是通过混淆的发行版代码,考虑使用DexGuard。DexGuard是有ProGuard团队作的一个商业软件。利用DexGuard,能够很容易的分割Dex文件,解决了65k方法空间限制的问题。

感谢

感谢Antti Lammi, Joni Karppinen, Peter Tackage, Timo Tuominen, Vera Izrailit, Vihtori Mäntylä, Mark Voit, Andre Medeiros, Paul Houghton和其余Futurice开发者分享关于安卓开发的知识。

许可

Futurice Oy Creative Commons Attribution 4.0 International (CC BY 4.0)

相关文章
相关标签/搜索