zoukankan      html  css  js  c++  java
  • 【安卓开发】最佳实践之一:安卓开发篇

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

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

    摘要

    • 使用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权限。

    编译系统

    编译系统首选Gradle。相比于Gradle,Ant更加的局限并且更加繁琐。使用Gradle编译系统可以很简单的做到:

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

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

    项目结构

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

    旧项目结构:

    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

     

    新的项目结构:

    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’文件夹,分别用于存放付费版应用代码和免费版应用的代码。

    顶层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)

  • 相关阅读:
    2014深圳华为机试剖析
    Tomcat J2ee 发布步骤
    Delphi实现无标题有边框的窗体
    所谓的湖南普天科技不是一般的坑
    struts2文件异步上传
    Adobe Flash Builder 4.7下载地址及破解补丁(32位&64位)
    从浏览器启动客户端程序
    delphi非IE内核浏览器控件TEmbeddedChrome下载|TEmbeddedChrome代码
    delphi中EmbeddedWB网页html相互调用(二)
    delphi中WEBBrowser网页html相互调用(一)
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157987.html
Copyright © 2011-2022 走看看