Showing preview only (351K chars total). Download the full file or copy to clipboard to get everything.
Repository: futurice/android-best-practices
Branch: master
Commit: d608f12971f6
Files: 11
Total size: 340.7 KB
Directory structure:
gitextract_mcniy78j/
├── LICENSE
├── README.md
└── translations/
├── Chinese/
│ └── README.cn.md
├── French/
│ └── README.fr.md
├── Japanese/
│ └── README.ja.md
├── Korean/
│ └── README.ko.md
├── Portuguese/
│ └── README.pt.md
├── Russian/
│ └── README.ru.md
├── Spanish/
│ └── README.es.md
├── Turkish/
│ └── README.tr.md
└── Vietnamese/
└── README.vi.md
================================================
FILE CONTENTS
================================================
================================================
FILE: LICENSE
================================================
Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible.
Using Creative Commons Public Licenses
Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses.
Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors.
Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public.
Creative Commons Attribution 4.0 International Public License
By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
Section 1 – Definitions.
Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
Licensor means the individual(s) or entity(ies) granting rights under this Public License.
Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
Section 2 – Scope.
License grant.
Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
reproduce and Share the Licensed Material, in whole or in part; and
produce, reproduce, and Share Adapted Material.
Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
Term. The term of this Public License is specified in Section 6(a).
Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
Downstream recipients.
Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
Other rights.
Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
Patent and trademark rights are not licensed under this Public License.
To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties.
Section 3 – License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the following conditions.
Attribution.
If You Share the Licensed Material (including in modified form), You must:
retain the following if it is supplied by the Licensor with the Licensed Material:
identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
a copyright notice;
a notice that refers to this Public License;
a notice that refers to the disclaimer of warranties;
a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License.
Section 4 – Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database;
if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and
You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
Section 5 – Disclaimer of Warranties and Limitation of Liability.
Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
Section 6 – Term and Termination.
This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
Section 7 – Other Terms and Conditions.
The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
Section 8 – Interpretation.
For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses.
Creative Commons may be contacted at creativecommons.org.
================================================
FILE: README.md
================================================
# Best practices in Android development
Avoid reinventing the wheel by following these guidelines. Lessons learned from Android developers in [Futurice](http://www.futurice.com). If you are interested in iOS or Windows Phone development, be sure to check also our [**iOS Good Practices**](https://github.com/futurice/ios-good-practices) and [**Windows App Development Best Practices**](https://github.com/futurice/windows-app-development-best-practices) documents.
[](https://android-arsenal.com/details/1/1091)
[](https://spiceprogram.org/)
## Summary
#### [Use Gradle and its default project structure](#build-system)
#### [Put passwords and sensitive data in gradle.properties](#gradle-configuration)
#### [Use the Jackson library to parse JSON data](#libraries)
#### [Don't write your own HTTP client, use OkHttp libraries](#networklibs)
#### [Avoid Guava and use only a few libraries due to the *65k method limit*](#methodlimitation)
#### [Sail carefully when choosing between Activities and Fragments](#activities-and-fragments)
#### [Layout XMLs are code, organize them well](#resources)
#### [Use styles to avoid duplicate attributes in layout XMLs](#styles)
#### [Use multiple style files to avoid a single huge one](#splitstyles)
#### [Keep your colors.xml short and DRY, just define the palette](#colorsxml)
#### [Also keep dimens.xml DRY, define generic constants](#dimensxml)
#### [Do not make a deep hierarchy of ViewGroups](#deephierarchy)
#### [Avoid client-side processing for WebViews, and beware of leaks](#webviews)
#### [Use JUnit for unit tests, Espresso for connected (UI) tests, and AssertJ-Android for easier assertions in your Android tests](#test-frameworks)
#### [Always use ProGuard or DexGuard](#proguard-configuration)
#### [Use SharedPreferences for simple persistence, otherwise ContentProviders](#data-storage)
#### [Use Stetho to debug your application](#use-stetho)
#### [Use Leak Canary to find memory leaks](#use-leakcanary)
#### [Use continuous integration](#use-continuous-integration-1)
----------
### Android SDK
Place your [Android SDK](https://developer.android.com/sdk/installing/index.html?pkg=tools) somewhere in your home directory or some other application-independent location. Some distributions of IDEs include the SDK when installed, and may place it under the same directory as the IDE. This can be bad when you need to upgrade (or reinstall) the IDE, as you may lose your SDK installation, forcing a long and tedious redownload.
Also avoid putting the SDK in a system-level directory that might need root permissions, to avoid permissions issues.
### Build system
Your default option should be [Gradle](https://gradle.org) using the [Android Gradle plugin](https://developer.android.com/studio/build/index.html).
It is important that your application's build process is defined by your Gradle files, rather than being reliant on IDE specific configurations. This allows for consistent builds between tools and better support for continuous integration systems.
### Project structure
Although Gradle offers a large degree of flexibility in your project structure, unless you have a compelling reason to do otherwise, you should accept its [default structure](https://developer.android.com/studio/build/index.html#sourcesets) as this simplify your build scripts.
### Gradle configuration
**General structure.** Follow [Google's guide on Gradle for Android](https://developer.android.com/studio/build/index.html).
**minSdkVersion: 21** We recommend to have a look at the [Android version usage chart](https://developer.android.com/about/dashboards/index.html#Platform) before defining the minimum API required. Remember that the statistics given are global statistics and may differ when targeting a specific regional/demographic market. It is worth mentioning that some material design features are only available on Android 5.0 (API level 21) and above. And also, from API 21, the multidex support library is not needed anymore.
**Small tasks.** Instead of (shell, Python, Perl, etc) scripts, you can make tasks in Gradle. Just follow [Gradle's documentation](http://www.gradle.org/docs/current/userguide/userguide_single.html#N10CBF) for more details. Google also provides some helpful [Gradle recipes](https://developer.android.com/studio/build/gradle-tips.html), specific to Android.
**Passwords.** In your app's `build.gradle` you will need to define the `signingConfigs` for the release build. Here is what you should avoid:
_Don't do this_. This would appear in the version control system.
```groovy
signingConfigs {
release {
// DON'T DO THIS!!
storeFile file("myapp.keystore")
storePassword "password123"
keyAlias "thekey"
keyPassword "password789"
}
}
```
Instead, make a `gradle.properties` file which should _not_ be added to the version control system:
```
KEYSTORE_PASSWORD=password123
KEY_PASSWORD=password789
```
That file is automatically imported by Gradle, so you can use it in `build.gradle` as such:
```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.")
}
}
}
```
**Prefer Maven dependency resolution to importing jar files.** If you explicitly include jar files in your project, they will be a specific frozen version, such as `2.1.1`. Downloading jars and handling updates is cumbersome and is a problem that Maven already solves properly. Where possible, you should attempt to use Maven to resolve your dependencies, for example:
```groovy
dependencies {
implementation 'com.squareup.okhttp:okhttp3:3.8.0'
}
```
**Avoid Maven dynamic dependency resolution**
Avoid the use of dynamic dependency versions, such as `2.1.+` as this may result in different and unstable builds or subtle, untracked differences in behavior between builds. The use of static versions such as `2.1.1` helps create a more stable, predictable and repeatable development environment.
**Use different package name for non-release builds**
Use `applicationIdSuffix` for *debug* [build type](http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Types) to be able to install both *debug* and *release* apk on the same device (do this also for custom build types, if you need any). This will be especially valuable after your app has been published.
```groovy
android {
buildTypes {
debug {
applicationIdSuffix '.debug'
versionNameSuffix '-DEBUG'
}
release {
// ...
}
}
}
```
Use different icons to distinguish the builds installed on a device—for example with different colors or an overlaid "debug" label. Gradle makes this very easy: with default project structure, simply put *debug* icon in `app/src/debug/res` and *release* icon in `app/src/release/res`. You could also [change app name](http://stackoverflow.com/questions/24785270/how-to-change-app-name-per-gradle-build-type) per build type, as well as `versionName` (as in the above example).
**Share debug app keystore file**
Sharing the debug APK keystore file via the app repository saves time when testing on shared devices and avoids the uninstalling/reinstalling of the app. It also simplifies the processing of working with some Android SDKs, such as Facebook, which require the registration of a single key store hash. Unlike the release key file, the debug key file can safely be added to your repository.
**Share code style formatting defintions**
Sharing the code style and formatting definitions via the app repository helps ensure a visually consistent code base and makes code comprehension and reviews easier.
### Android Studio as your main IDE
The recommended IDE for Android development is [Android Studio](https://developer.android.com/sdk/installing/studio.html) because it is developed and constantly updated by Google, has good support for Gradle, contains a range of useful monitoring and analysis tools and is fully tailored for Android development.
Avoid adding Android Studio's specific configuration files, such as `.iml` files to the version control system as these often contain configurations specific of your local machine, which won't work for your colleagues.
### Libraries
**[Jackson](http://wiki.fasterxml.com/JacksonHome)** is a Java library for JSON serialization and deserialization, it has a wide-scoped and versatile API, supporting various ways of processing JSON: streaming, in-memory tree model, and traditional JSON-POJO data binding.
[Gson](https://code.google.com/p/google-gson/) is another popular choice and being a smaller library than Jackson, you might prefer it to avoid 65k methods limitation. Also, if you are using
[Moshi](https://github.com/square/moshi), another of [Square's](https://github.com/square) open source libraries, builds upon learnings from the development of Gson and also integrates well with Kotlin.
<a name="networklibs"></a>
**Networking, caching, and images.** There are a couple of battle-proven solutions for performing requests to backend servers, which you should use rather than implementing your own client. We recommend basing your stack around [OkHttp](http://square.github.io/okhttp/) for efficient HTTP requests and using [Retrofit](http://square.github.io/retrofit/) to provide a typesafe layer. If you choose Retrofit, consider [Picasso](http://square.github.io/picasso/) for loading and caching images.
Retrofit, Picasso and OkHttp are created by the same company, so they complement each other nicely and compatability issues are uncommon.
[Glide](https://github.com/bumptech/glide) is another option for loading and caching images. It has support for animated GIFs, circular images and claims of better performance than Picasso, but also a bigger method count.
**RxJava** is a library for Reactive Programming, in other words, handling asynchronous events. It is a powerful paradigm, but it also has a steep learning curve. We recommend taking some caution before using this library to architect the entire application. We have written some blog posts on it: [[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). For a reference app, our open source app [Freesound Android](https://github.com/futurice/freesound-android) makes extensive use of RxJava 2.
If you have no previous experience with Rx, start by applying it only for responses from app's backend APIs. Alternatively, start by applying it for simple UI event handling, like click events or typing events on a search field. If you are confident in your Rx skills and want to apply it to the whole architecture, then write documentation on all the tricky parts. Keep in mind that another programmer unfamiliar to RxJava might have a very hard time maintaining the project. Do your best to help them understand your code and also Rx.
Use [RxAndroid](https://github.com/ReactiveX/RxAndroid) for Android threading support and [RxBinding](https://github.com/JakeWharton/RxBinding) to easily create Observables from existing Android components.
**[Retrolambda](https://github.com/evant/gradle-retrolambda)** is a Java library for using Lambda expression syntax in Android and other pre-JDK8 platforms. It helps keep your code tight and readable especially if you use a functional style, such as in RxJava.
Android Studio offers code assist support for Java 8 lambdas. If you are new to lambdas, just use the following to get started:
- Any interface with just one method is "lambda friendly" and can be folded into the more tight syntax
- If in doubt about parameters and such, write a normal anonymous inner class and then let Android Studio fold it into a lambda for you.
Note that from Android Studio 3.0, [Retrolambda is no longer required](https://developer.android.com/studio/preview/features/java8-support.html).
<a name="methodlimitation"></a>
**Beware of the dex method limitation, and avoid using many libraries.** Android apps, when packaged as a dex file, have a hard limitation of 65536 referenced methods [[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/). You will see a fatal error on compilation if you pass the limit. For that reason, use a minimal amount of libraries, and use the [dex-method-counts](https://github.com/mihaip/dex-method-counts) tool to determine which set of libraries can be used in order to stay under the limit. Especially avoid using the Guava library, since it contains over 13k methods.
### Activities and Fragments
There is no consensus among the community nor Futurice developers how to best organize Android architectures with Fragments and Activities. Square even has [a library for building architectures mostly with Views](https://github.com/square/mortar), bypassing the need for Fragments, but this still is not considered a widely recommendable practice in the community.
Because of Android API's history, you can loosely consider Fragments as UI pieces of a screen. In other words, Fragments are normally related to UI. Activities can be loosely considered to be controllers, they are especially important for their lifecycle and for managing state. However, you are likely to see variation in these roles: activities might take UI roles ([delivering transitions between screens](https://developer.android.com/about/versions/lollipop.html)), and [fragments might be used solely as controllers](http://developer.android.com/guide/components/fragments.html#AddingWithoutUI). We suggest you sail carefully, making informed decisions since there are drawbacks for choosing a fragments-only architecture, or activities-only, or views-only. Here is some advice on what to be careful with, but take them with a grain of salt:
- Avoid using [nested fragments](https://developer.android.com/about/versions/android-4.2.html#NestedFragments) extensively, because [matryoshka bugs](http://delyan.me/android-s-matryoshka-problem/) can occur. Use nested fragments only when it makes sense (for instance, fragments in a horizontally-sliding ViewPager inside a screen-like fragment) or if it's a well-informed decision.
- Avoid putting too much code in Activities. Whenever possible, keep them as lightweight containers, existing in your application primarily for the lifecycle and other important Android-interfacing APIs. Prefer single-fragment activities instead of plain activities - put UI code into the activity's fragment. This makes it reusable in case you need to change it to reside in a tabbed layout, or in a multi-fragment tablet screen. Avoid having an activity without a corresponding fragment, unless you are making an informed decision.
### Java packages structure
We recommend using a *feature based* package structure for your code. This has the following benefits:
- Clearer feature dependency and interface boundaries.
- Promotes encapsulation.
- Easier to understand the components that define the feature.
- Reduces risk of unknowingly modifying unrelated or shared code.
- Simpler navigation: most related classes will be in the one package.
- Easier to remove a feature.
- Simplifies the transition to module based build structure (better build times and Instant Apps support)
The alternative approach of defining your packages by *how* a feature is built (by placing related Activities, Fragments, Adapters etc in separate packages) can lead to a fragmented code base with less implementation flexibility. Most importantly, it hinders your ability to comprehend your code base in terms of its primary role: to provide features for your app.
### Resources
**Naming.** Follow the convention of prefixing the type, as in `type_foo_bar.xml`. Examples: `fragment_contact_details.xml`, `view_primary_button.xml`, `activity_main.xml`.
**Organizing layout XMLs.** If you're unsure how to format a layout XML, the following convention may help.
- One attribute per line, indented by 4 spaces
- `android:id` as the first attribute always
- `android:layout_****` attributes at the top
- `style` attribute at the bottom
- Tag closer `/>` on its own line, to facilitate ordering and adding attributes.
- Rather than hard coding `android:text`, consider using [Designtime attributes](http://tools.android.com/tips/layout-designtime-attributes) available for Android Studio.
```xml
<?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>
```
As a rule of thumb, attributes `android:layout_****` should be defined in the layout XML, while other attributes `android:****` should stay in a style XML. This rule has exceptions, but in general works fine. The idea is to keep only layout (positioning, margin, sizing) and content attributes in the layout files, while keeping all appearance details (colors, padding, font) in styles files.
The exceptions are:
- `android:id` should obviously be in the layout files
- `android:orientation` for a `LinearLayout` normally makes more sense in layout files
- `android:text` should be in layout files because it defines content
- Sometimes it will make sense to make a generic style defining `android:layout_width` and `android:layout_height` but by default these should appear in the layout files
<a name="styles"></a>
**Use styles.** Almost every project needs to properly use styles, because it is very common to have a repeated appearance for a view. At least you should have a common style for most text content in the application, for example:
```xml
<style name="ContentText">
<item name="android:textSize">@dimen/font_normal</item>
<item name="android:textColor">@color/basic_black</item>
</style>
```
Applied to TextViews:
```xml
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/price"
style="@style/ContentText"
/>
```
You probably will need to do the same for buttons, but don't stop there yet. Go beyond and move a group of related and repeated `android:****` attributes to a common style.
<a name="splitstyles"></a>
**Split a large style file into other files.** You don't need to have a single `styles.xml` file. Android SDK supports other files out of the box, there is nothing magical about the name `styles`, what matters are the XML tags `<style>` inside the file. Hence you can have files `styles.xml`, `styles_home.xml`, `styles_item_details.xml`, `styles_forms.xml`. Unlike resource directory names which carry some meaning for the build system, filenames in `res/values` can be arbitrary.
<a name="colorsxml"></a>
**`colors.xml` is a color palette.** There should be nothing in your `colors.xml` other than a mapping from a color name to an RGBA value. This helps avoid repeating RGBA values and as such will make it easy to change or refactor colors, and also will make it explicit how many different colors are being used. Normally for a aesthetic UI, it is important to reduce the variety of colors being used.
*So, don't define your colors.xml like this:*
```xml
<resources>
<color name="button_foreground">#FFFFFF</color>
<color name="button_background">#2A91BD</color>
</resources>
```
Instead, do this:
```xml
<resources>
<!-- grayscale -->
<color name="white">#FFFFFF</color>
<!-- basic colors -->
<color name="blue">#2A91BD</color>
</resources>
```
Ask the designer of the application for this palette. The names do not need to be plain color names as "green", "blue", etc. Names such as "brand_primary", "brand_secondary", "brand_negative" are totally acceptable as well.
By referencing the color palette from your styles allows you to abstract the underlying colors from their usage in the app, as per:
- `colors.xml` - defines only the color palette.
- `styles.xml` - defines styles which reference the color palette and reflects the color usage. (e.g. the button foreground is white).
- `activity_main.xml` - references the appropriate style in `styles.xml` to color the button.
If needed, even further separation between underlying colors and style usage can be achieved by defined an additional color resource file which references the color palette. As per:
```xml
<color name="button_foreground">@color/white</color>
<color name="button_background">@color/blue</color>
```
Then in styles.xml:
```xml
<style name="AcceptButton">
<item name="android:foreground">@color/button_foreground</item>
<item name="android:background">@color/button_background</item>
</style>
```
This approach offers improved color refactoring and more stable style definitions when multiple related styles share similar color and usage properties. However, it comes at the cost of maintaining another set of color mappings.
<a name="dimensxml"></a>
**Treat dimens.xml like colors.xml.** You should also define a "palette" of typical spacing and font sizes, for basically the same purposes as for colors. A good example of a dimens file:
```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>
```
You should use the `spacing_****` dimensions for layouting, in margins and paddings, instead of hard-coded values, much like strings are normally treated. This will give a consistent look-and-feel, while making it easier to organize and change styles and layouts.
**strings.xml**
Name your strings with keys that resemble namespaces, and don't be afraid of repeating a value for two or more keys. Languages are complex, so namespaces are necessary to bring context and break ambiguity.
**Bad**
```xml
<string name="network_error">Network error</string>
<string name="call_failed">Call failed</string>
<string name="map_failed">Map loading failed</string>
```
**Good**
```xml
<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>
```
Don't write string values in all uppercase. Stick to normal text conventions (e.g., capitalize first character). If you need to display the string in all caps, then do that using for instance the attribute [`textAllCaps`](http://developer.android.com/reference/android/widget/TextView.html#attr_android:textAllCaps) on a TextView.
**Bad**
```xml
<string name="error_message_call">CALL FAILED</string>
```
**Good**
```xml
<string name="error_message_call">Call failed</string>
```
<a name="deephierarchy"></a>
**Avoid a deep hierarchy of views.** Sometimes you might be tempted to just add yet another LinearLayout, to be able to accomplish an arrangement of views. This kind of situation may occur:
```xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<RelativeLayout
...
>
<LinearLayout
...
>
<LinearLayout
...
>
<LinearLayout
...
>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
```
Even if you don't witness this explicitly in a layout file, it might end up happening if you are inflating (in Java) views into other views.
A couple of problems may occur. You might experience performance problems, because there is a complex UI tree that the processor needs to handle. Another more serious issue is a possibility of [StackOverflowError](http://stackoverflow.com/questions/2762924/java-lang-stackoverflow-error-suspected-too-many-views).
Therefore, try to keep your views hierarchy as flat as possible: learn how to use [ConstraintLayout](https://developer.android.com/training/constraint-layout/index.html), how to [optimize your layouts](http://developer.android.com/training/improving-layouts/optimizing-layout.html) and to use the [`<merge>` tag](http://stackoverflow.com/questions/8834898/what-is-the-purpose-of-androids-merge-tag-in-xml-layouts).
<a name="webviews"></a>
**Beware of problems related to WebViews.** When you must display a web page, for instance for a news article, avoid doing client-side processing to clean the HTML, rather ask for a "*pure*" HTML from the backend programmers. [WebViews can also leak memory](http://stackoverflow.com/questions/3130654/memory-leak-in-webview) when they keep a reference to their Activity, instead of being bound to the ApplicationContext. Avoid using a WebView for simple texts or buttons, prefer the platform's widgets.
### Test Frameworks
**Use [JUnit](https://developer.android.com/training/testing/unit-testing/local-unit-tests.html) for unit testing** Plain, Android dependency-free unit testing on the JVM is best done using [Junit](https://junit.org).
**Avoid [Robolectric](http://robolectric.org/)** Prior to the improved support for JUnit in the Android build system, Robolectric was promoted as a test framework seeking to provide tests "disconnected from device" for the sake of development speed. However, testing under Robolectric is inaccurate and incomplete as it works by providing mock implementations of the Android platform, which provides no guarantees of correctness. Instead, use a combination of JVM based unit tests and dedicated on-device integration tests.
**[Espresso](https://developer.android.com/training/testing/ui-testing/espresso-testing.html) makes writing UI tests easy.**
**[AssertJ-Android](http://square.github.io/assertj-android/) an AssertJ extension library making assertions easy in Android tests** Assert-J comes modules easier for you to test Android specific components, such as the Android Support, Google Play Services and Appcompat libraries.
A test assertion will look like:
```java
// Example assertion using AssertJ-Android
assertThat(layout).isVisible()
.isVertical()
.hasChildCount(5);
```
### Emulators
The performance of the Android SDK emulator, particularly the x86 variant, has improvement markedly in recent years and is now adequate for most day-to-day development scenarios. However, you should not discount the value of ensuring your application behaves correctly on real devices. Of course, testing on all possible devices is not practical, so rather focus your efforts on devices with a large market share and those most relevant to your app.
### Proguard configuration
[ProGuard](http://proguard.sourceforge.net/) is normally used on Android projects to shrink and obfuscate the packaged code.
Whether you are using ProGuard or not depends on your project configuration. Usually you would configure Gradle to use ProGuard when building a release APK.
```groovy
buildTypes {
debug {
minifyEnabled false
}
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
```
In order to determine which code has to be preserved and which code can be discarded or obfuscated, you have to specify one or more entry points to your code. These entry points are typically classes with main methods, applets, midlets, activities, etc.
Android framework uses a default configuration which can be found from `SDK_HOME/tools/proguard/proguard-android.txt`. Using the above configuration, custom project-specific ProGuard rules, as defined in `my-project/app/proguard-rules.pro`, will be appended to the default configuration.
A common problem related to ProGuard is to see the application crashing on startup with `ClassNotFoundException` or `NoSuchFieldException` or similar, even though the build command (i.e. `assembleRelease`) succeeded without warnings.
This means one out of two things:
1. ProGuard has removed the class, enum, method, field or annotation, considering it's not required.
2. ProGuard has obfuscated (renamed) the class, enum or field name, but it's being used indirectly by its original name, i.e. through Java reflection.
Check `app/build/outputs/proguard/release/usage.txt` to see if the object in question has been removed.
Check `app/build/outputs/proguard/release/mapping.txt` to see if the object in question has been obfuscated.
In order to prevent ProGuard from *stripping away* needed classes or class members, add a `keep` options to your ProGuard config:
```
-keep class com.futurice.project.MyClass { *; }
```
To prevent ProGuard from *obfuscating* classes or class members, add a `keepnames`:
```
-keepnames class com.futurice.project.MyClass { *; }
```
Read more at [Proguard](https://www.guardsquare.com/en/proguard/manual/examples) for examples.
**Early in your project, make and test release build** to check whether ProGuard rules are correctly retaining your dependencies. Also whenever you include new libraries or update their dependencies, make a release build and test the APK on a device. Don't wait until your app is finally version "1.0" to make a release build, you might get several unpleasant surprises and a short time to fix them.
**Tip.** Save the `mapping.txt` file for every release that you publish to your users. By retaining a copy of the `mapping.txt` file for each release build, you ensure that you can debug a problem if a user encounters a bug and submits an obfuscated stack trace.
**DexGuard**. If you need hard-core tools for optimizing, and specially obfuscating release code, consider [DexGuard](http://www.saikoa.com/dexguard), a commercial software made by the same team that built ProGuard. It can also easily split Dex files to solve the 65k methods limitation.
### Data storage
#### SharedPreferences
If you only need to persist simple values and your application runs in a single process SharedPreferences is probably enough for you. It is a good default option.
There are some situations where SharedPreferences are not suitable:
* *Performance*: Your data is complex or there is a lot of it
* *Multiple processes accessing the data*: You have widgets or remote services that run in their own processes and require synchronized data
* *Relational data* Distinct parts of your data are relational and you want to enforce that those relationships are maintained.
You can also store more complex objects by serializing them to JSON to store them and deserializing them when retrieving. You should consider the tradeoffs when doing this as it may not be particularly performant, nor maintainable.
#### ContentProviders
In case SharedPreferences are not enough, you should use the platform standard ContentProviders, which are fast and process safe.
The single problem with ContentProviders is the amount of boilerplate code that is needed to set them up, as well as low quality tutorials. It is possible, however, to generate the ContentProvider by using a library such as [Schematic](https://github.com/SimonVT/schematic), which significantly reduces the effort.
You still need to write some parsing code yourself to read the data objects from the Sqlite columns and vice versa. It is possible to serialize the data objects, for instance with Gson, and only persist the resulting string. In this way you lose in performance but on the other hand you do not need to declare a column for all the fields of the data class.
#### Using an ORM
We generally do not recommend using an Object-Relation Mapping library unless you have unusually complex data and you have a dire need. They tend to be complex and require time to learn. If you decide to go with an ORM you should pay attention to whether or not it is _process safe_ if your application requires it, as many of the existing ORM solutions surprisingly are not.
### Use Stetho
[Stetho](http://facebook.github.io/stetho/) is a debug bridge for Android applications from Facebook that integrates with the Chrome desktop browser's Developer Tools. With Stetho you can easily inspect your application, most notably the network traffic. It also allows you to easily inspect and edit SQLite databases and the shared preferences in your app. You should, however, make sure that Stetho is only enabled in the debug build and not in the release build variant.
Another alternative is [Chuck](https://github.com/jgilfelt/chuck) which, although offering slightly more simplified functionality, is still useful for testers as the logs are displayed on the device, rather than in the more complicated connected Chrome browser setup that Stetho requires.
### Use LeakCanary
[LeakCanary](https://github.com/square/leakcanary) is a library that makes runtime detection and identification of memory leaks a more routine part of your application development process. See the library [wiki](https://github.com/square/leakcanary/wiki) for details on configuration and usage. Just remember to configure only the "no-op" dependency in your release build!
### Use continuous integration
Continuous integration systems let you automatically build and test your project every time you push updates to version control. Continuous integration also runs static code analysis tools, generates the APK files and distributes them.
[Lint](https://developer.android.com/studio/write/lint.html) and [Checkstyle](http://checkstyle.sourceforge.net/) are tools that ensure the code quality while [Findbugs](http://findbugs.sourceforge.net/) looks for bugs in the code.
There is a wide variety of continuous integration software which provide different features. Pricing plans might be for free if your project is open-sourced.
[Jenkins](https://jenkins.io/) is a good option if you have a local server at your disposal, on the other hand [Travis CI](https://travis-ci.org/) is also a recommended choice if you plan to use a cloud-based continuous integration service.
### 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.
### Acknowledgements
<p align="center">
<img alt="logo" src="https://raw.githubusercontent.com/futurice/spiceprogram/gh-pages/assets/img/logo/chilicorn_no_text-256.png" width="220"/>
</p>
This project is sponsored by [**Spice Program**](http://spiceprogram.org/chilicorn-history/), our open source and social impact program made with love by [**Futurice**](http://www.futurice.com).
### License
[Futurice Oy](http://www.futurice.com)
Creative Commons Attribution 4.0 International (CC BY 4.0)
================================================
FILE: translations/Chinese/README.cn.md
================================================
# Android 开发最佳实践
从[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
<?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_width` 和 `android:layout_height`属性放到一个style中作为一个通用的风格中更有意义,但是默认情况下这些应该放到layout文件中。
**使用styles** 几乎每个项目都需要适当的使用style文件,因为对于一个视图来说有一个重复的外观是很常见的。
在应用中对于大多数文本内容,最起码你应该有一个通用的style文件,例如:
```xml
<style name="ContentText">
<item name="android:textSize">@dimen/font_normal</item>
<item name="android:textColor">@color/basic_black</item>
</style>
```
应用到TextView 中:
```xml
<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值。
*不要这样做*
```xml
<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`文件中。
相反,这样做:
```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文件** 与定义颜色调色板一样,你同时也应该定义一个空隙间隔和字体大小的“调色板”。
一个好的例子,如下所示:
```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。你可能使用这种方法解决:
```xml
<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](http://stackoverflow.com/questions/2762924/java-lang-stackoverflow-error-suspected-too-many-views).
因此尽量保持你的视图tree:学习如何使用[RelativeLayout](https://developer.android.com/guide/topics/ui/layout/relative.html),
如何 [optimize 你的布局](http://developer.android.com/training/improving-layouts/optimizing-layout.html) 和如何使用
[`<merge>` 标签](http://stackoverflow.com/questions/8834898/what-is-the-purpose-of-androids-merge-tag-in-xml-layouts).
**小心关于WebViews的问题.** 如果你必须显示一个web视图,
比如说对于一个新闻文章,避免做客户端处理HTML的工作,
最好让后端工程师协助,让他返回一个 "*纯*" HTML。
当绑定WebViews到引用它的Activity,而不是绑定到ApplicationContext时。
[WebViews 也能导致内存泄露](http://stackoverflow.com/questions/3130654/memory-leak-in-webview)。
当使用简单的文字或按钮时,避免使用WebView,这时使用TextView或Buttons更好。
### 测试框架
Android SDK的测试框架还处于初级阶段,特别是关于UI测试方面。Android Gradle
目前实现了一个叫[`connectedAndroidTest`](http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Testing)的测试,
它[使用一个JUnit 为Android提供的扩展插件 extension of JUnit with helpers for Android](http://developer.android.com/reference/android/test/package-summary.html).可以跑你生成的JUnit测试,
**只当做单元测试时使用 [Robolectric](http://robolectric.org/) ,views 不用**
它是一个最求提供"不连接设备的"为了加速开发的测试,
非常时候做 models 和 view models 的单元测试。
然而,使用Robolectric测试时不精确的,也不完全对UI测试。
当你对有关动画的UI元素、对话框等,测试时会有问题,
这主要是因为你是在 “在黑暗中工作”(在没有可控的界面情况下测试)
**[Robotium](https://code.google.com/p/robotium/) 使写UI测试非常简单。
** 对于UI测试你不需 Robotium 跑与设备连接的测试。
但它可能会对你有益,是因为它有许多来帮助类的获得和分析视图,控制屏幕。
测试用例看起来像这样简单:
```java
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"));
```
### 模拟器
如果你全职开发Android App,那么买一个[Genymotion emulator](http://www.genymotion.com/)license吧。
Genymotion 模拟器运行更快的秒帧的速度,比起典型的AVD模拟器。他有演示你APP的工具,高质量的模拟网络连接,GPS位置,等等。它同时还有理想的连接测试。
你若涉及适配使用很多不同的设备,买一个Genymotion 版权是比你买很多真设备便宜多的。
注意:Genymotion模拟器没有装载所有的Google服务,如Google Play Store和Maps。你也可能需
要测试Samsung指定的API,若这样的话你还是需要购买一个真实的Samsung设备。
### 混淆配置
[ProGuard](http://proguard.sourceforge.net/) 是一个在Android项目中广泛使用的压缩和混淆打包的源码的工具。
你是否使用ProGuard取决你项目的配置,当你构建一个release版本的apk时,通常你应该配置gradle文件。
```groovy
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 { *; }
```
更多例子请参考[Proguard](http://proguard.sourceforge.net/#manual/examples.html)。
**在构建项目之初,发布一个版本** 来检查ProGuard规则是否正确的保持了重要的部分。
同时无论何时你添加了新的类库,做一个发布版本,同时apk在设备上跑起来测试一下。
不要等到你的app要发布 "1.0"版本了才做版本发布,那时候你可能会碰到好多意想不到的异常,需要一些时间去修复他们。
**Tips**每次发布新版本都要写 `mapping.txt`。每发布一个版本,如果用户遇到一个bug,同时提交了一个混淆过的堆栈跟踪。
通过保留`mapping.txt`文件,来确定你可以调试的问题。
**DexGuard** 如果你需要核心工具来优化,和专门混淆的发布代码,考虑使用[DexGuard](http://www.saikoa.com/dexguard),
一个商业软件,ProGuard 也是有他们团队开发的。
它会很容易将Dex文件分割,来解决65K个方法限制问题。
###数据存储
**SharedPreferences**
如果你只是需要持久化存储简单的标记位,并且你的应用运行在单一进程,那么SharedPreferences可能就满足了你的需求。它是一个非常好的选择。
这里有两个使你可能不使用SharedPreferences的原因:
- Performance: Your data is complex or there is a lot of it
- 性能问题:你的很多数据结构负责的数据需要存储。
- Multiple processes accessing the data: You have widgets or remote services that run in their own processes and require synchronized data
- 多线程访问数据:你有多个控件或者运行在各自线程上的远程的服务需要同步数据。
**ContentProviders**
如果SharedPreferences不足以满足你的需求,那么你可以使用平台标准的ContentProviders,它不仅快速,并且线程安全。
使用ContentProviders的唯一问题是建立他们需要大量的模板代码,并且少有高质量的教程。如果可以,我们可以通过使用第三方库Schematic,极大降低了冗余操作,去生成ContentProviders.
你可能仍然需要亲自写一些解析代码去从Sqlite读取数据对象,或者进行相反的操作。如果可以序列化数据对象,例如通过Gson,只持久化存储最终是字符串。通过这种方式虽然会降低性能,但是从另一个角度来讲,你不需要为每一个数据结构声明表结构。
**使用ORM**我们通常不推荐使用对象关系映射第三方库除非你有非常复杂的数据结构,并且你确定你真的需要它。他们通常比较复杂,并且需要时间去学习。如果你决定了在你的应用中使用ORM,你应该注意它是否是线程安全的,而对于目前大多数ORM解决方案都是非线程安全的。
**使用Stetho**Stetho 是一个Facebook 开源的Android调试工具,它是Chrome Developer Tools的扩展。通过它可以检测应用的网络情况。它也允许你可以检测应用的数据库,shared preferences。但是,你应该确保Stetho只有在Debug状态下得以开启,而不是在正式发布版本中。
**使用LeakCanary**LeakCanary 是可以在应用运行中检测,定位内存泄露的Java库。使用它应是你开发应用过程中的一部分。更多详细的配置和使用情况请参照wiki。你只需要记得它在你的正式版本中你是不需要配置的。
### 致谢
感谢Antti Lammi, Joni Karppinen, Peter Tackage, Timo Tuominen, Vera Izrailit, Vihtori Mäntylä, Mark Voit, Andre Medeiros, Paul Houghton 这些人和Futurice 开发者分享他们的Android开发经验。
### License
[Futurice Oy](www.futurice.com)
Creative Commons Attribution 4.0 International (CC BY 4.0)
### Translation
Translated to Chinese by [andyiac](https://github.com/andyiac)
================================================
FILE: translations/French/README.fr.md
================================================
# Les bonnes pratiques du développement Android
Leçons apprises par les developpeurs Android de [Futurice](http://www.futurice.com). Ne réinventez plus la roue en suivant ces bonnes pratiques. Si vous êtes intéressé par le développement iOS ou Windows Phone, n'hésitez pas à regarder nos documents [**Bonnes pratiques iOS**](https://github.com/futurice/ios-good-practices) et [**Bonnes pratiques Windows Phone**](https://github.com/futurice/windows-app-development-best-practices).
[](https://android-arsenal.com/details/1/1091)
## Sommaire
#### Utiliser Gradle et sa structure de projet recommandée
#### Mettre les mots de passe et les données sensibles dans le fichier gradle.properties
#### Ne pas écrire son propre client HTTP, utiliser les bibliothèques Volley ou OkHttp
#### Utiliser la librairie Jackson pour parser les données au format JSON
#### Eviter d'utiliser Guava et ne pas trop utiliser de libraries à cause de la *limite des 65k méthodes*
#### Utiliser des fragments pour représenter une interface graphique
#### Utiliser les activités uniquement pour gérer les fragments
#### Les fichiers XMLs sont aussi du code, penser à bien les organiser
#### Utiliser des styles pour éviter des duplicatas d'attributs dans les fichiers XMLs
#### Utiliser plusieurs fichiers de style pour éviter d'en avoir un seul trop gros
#### Garder le fichier colors.xml court et propre, se contenter de définir la palette de couleurs
#### Garder aussi le fichier dimens.xml concis, définir des constantes génériques
#### Ne pas imbriquer trop de ViewGroups les uns dans les autres
#### Eviter le traitement côté client pour les WebViews, et faire attention aux fuites
#### Utiliser Robolectric pour les tests unitaires, Robotium pour les tests graphiques
#### Utiliser Genymotion comme émulateur
#### Toujours utiliser Proguard et DexGuard
----------
### Android SDK
Mettre le [SDK Android](https://developer.android.com/sdk/installing/index.html?pkg=tools) dans votre répertoire utilisateur (home) ou à un autre endroit indépendant des applications. Certains IDEs inclus le SDK à leur installation, et peuvent le placer dans le même dossier que celui de l'IDE. Cela peut être problématique lorsqu'il faut mettre à jour (ou réinstaller) l'IDE, ou lorsque l'on change d'IDE. De plus éviter de mettre le SDK dans un dossier système qui pourrait nécessiter des permissions administrateurs si votre IDE n'est pas lancé en tant qu'administrateur.
### Système de build
Votre option par défaut devrait être [Gradle](http://tools.android.com/tech-docs/new-build-system). Ant est bien plus limité and aussi plus verbeux. Avec Gradle, il est simple de :
- Builder différentes _flavours_ ou variantes de votre application
- Ecrire des scripts pour réaliser des tâches.
- Gérer et télécharger les dépendences
- Customiser les clés de Store
- Et plus
De plus le plugin développé par Google, Android Gradle, a pour but de devenir le nouveau standard de système de build.
### Structure d'un projet
Il a deux options populaires : l'ancienne structure de projet Ant & Eclipse ADT et la nouvelle Gradle & Android Studio. Vous devriez choisir la nouvelle structure de projet. Si votre projet utilise la vieille structure, vous devriez migrer vers la nouvelle.
Ancienne structure:
```
ancienne-structure
├─ assets
├─ libs
├─ res
├─ src
│ └─ com/futurice/project
├─ AndroidManifest.xml
├─ build.gradle
├─ project.properties
└─ proguard-rules.pro
```
Nouvelle structure:
```
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
```
La principale différence est que la nouvelle structure sépare explicitement `source sets` (`main`, `androidTest`), résultant d'un des concepts de Gradle. Vous pourriez par exemple ajouter deux dossiers `payant` `gratuit` dans le dossier `src`qui aura donc le code source de la version payante et de la version gratuite de votre application.
Le fait d'avoir un dossier global `app` permet de distinguer facilement votre application d'autre libraries (ex.: `library-foobar`) qui seraient référencées dans votre application. Le fichier `settings.gradle` garde les références de ces bibliothèques qui pourront ensuite être référencées dans `app/build.gradle`.
### Configuration de Gradle
**Structure générale.** Se référencer à la documentation [Google's guide on Gradle for Android](http://tools.android.com/tech-docs/new-build-system/user-guide)
**Petites tâches.** A la place d'écrire des scripts (shell, Python, Perl, etc), faites des tâches dans Gradle. Se référencer à la documation [Gradle's documentation](http://www.gradle.org/docs/current/userguide/userguide_single.html#N10CBF) pour plus de détails.
**Mots de passe.** Dans le fichier `build.gradle` de votre application, vous aurez besoin de définir `signingConfigs` pour votre Release. Voici ce que vous devez éviter de faire :
_Ne faites pas ça_. Ceci apparaitrait dans le système de contrôle de version (Git par exemple).
```groovy
signingConfigs {
release {
storeFile file("myapp.keystore")
storePassword "password123"
keyAlias "thekey"
keyPassword "password789"
}
}
```
A la place, faites un fichier `gradle.properties` qui ne devrait _pas_ apparaître dans le système de contrôle de version.
```
KEYSTORE_PASSWORD=password123
KEY_PASSWORD=password789
```
Ce fichier est automatiquement importer par gradle, donc vous pouvez l'utiliser dans `build.gradle` comme ceci:
```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.")
}
}
}
```
**Préférez la résolution de dépendences Maven plutôt que l'importation de fichiers .jar.** Si vous importez explicitement des fichiers .jars dans votre projet, ceux-ci seront fixés dans une version, par exemple `2.1.1`. Télécharger et s'occuper des mises à jour est un tâche lourde et rébarbative à effectuer, Maven résoud ce problème d'une facçon élégante. Par exemple:
```groovy
dependencies {
implementation 'com.squareup.okhttp:okhttp:2.2.0'
implementation 'com.squareup.okhttp:okhttp-urlconnection:2.2.0'
}
```
**Eviter les résolutions dynamiques de dépendence Maven**
Evitez l'utilisation de versions dynamiques des bibliothèques comme `2.1.+` car cela pourrait mener à des builds de votre application instables ou à des différénces subtiles du comportement de votre application entre vos différents builds. L'utilisation de versions statiques des bibliothèques comme `2.1.1` permet de créer des environnements de développement plus stables et prédictibles.
### IDEs et éditeurs de texte/code
**Vous pouvez utiliser n'importe quel éditeur de code, il doit juste pouvoir respecter la structure du projet.** Utiliser tel ou tel éditeur de texte/code est un choix personnel, et il en va de votre responsabilité de choisir un étideur compatible avec la structure de projet et le système de build.
L'IDE le plus recommendé en ce moment est : [Android Studio](https://developer.android.com/sdk/installing/studio.html), car il est développé par Google, a bien intégré Gradle, et utilise la nouvelle structure de projet par défaut. De plus il est enfin en version stable et est conçu exprès pour le développement Android.
Vous pouvez utiliser [Eclipse ADT](https://developer.android.com/sdk/installing/index.html?pkg=adt) si vous le souhaitez, mais vous devrez le configurer car de base il utilise l'ancienne structure de projet et Ant pour builder votre application. Dans ce cas vous devrez utiliser Gradle et `adb` en ligne de commande. Si l'intégration de Gradle dans Eclipse ne marche pas chez vous, vos options sont d'utiliser les lignes de commande juste pour builder ou bien de migrer vers Android Studio. Cette dernière est la meilleure car le plugin ADT est devenu déprécié récemment.
Peu importe ce que vous utilisez, veuillez vous assurer que vous avez la nouvelle structure de projet, que vous utilisez Gradle et que vous n'introduisez pas de fichiers de configuation spécifique à votre éditeur de code dans le système de contrôle de version. Par exemple évitez d'ajouter un fichier de configuration Ant `build.xml`. Notamment n'oubliez pas de mettre à jour `build.gradle` si vous changez de configuration Ant. Enfin soyez sympas avec les autres développeurs et ne les forcez pas à changer leurs outils de développement ou leurs préférences.
### Bibliothèques
**[Jackson](http://wiki.fasterxml.com/JacksonHome)** est une librairie capable de convertir les Objets en JSON et vice-versa. [Gson](https://code.google.com/p/google-gson/) est une librairie similaire et aussi populaire cependant nous trouvons que Jackson est plus performante car elle supporte différentes façons de traiter le JSON : en streaming, avec une structure d'arbre et le mapping traditionnel JSON-POJO. Garder en tête toutefois que Jackson est une librairie plus conséquente que GSON donc selon votre cas vous serez peut-être amené à choisir GSON pour éviter la limitation des 65k méthodes. Autres alternatives : [Json-smart](https://code.google.com/p/json-smart/) et [Boon JSON](https://github.com/RichardHightower/boon/wiki/Boon-JSON-in-five-minutes)
**Réseaux, cache et images.** Il existe plusieurs solutions pour faire des requètes sur un backend. Vous pouvez utiliser [Volley](https://android.googlesource.com/platform/frameworks/volley) ou [Retrofit](http://square.github.io/retrofit/). Volley apporte aussi des helpers permettant de charger et mettre en cache des images. Si vous choisissez Retrofit, nous vous conseillons d'utiliser [Picasso](http://square.github.io/picasso/) pour charger et mettre en cache les images et [OkHttp](http://square.github.io/okhttp/) pour faire des requètes HTTP performantes. Les trois bibliothèques Retrofit, Picasso et OkHttp ont été créees par la même entreprise donc elles se complètent plutôt bien. [OkHttp can also be used in connection with Volley](http://stackoverflow.com/questions/24375043/how-to-implement-android-volley-with-okhttp-2-0/24951835#24951835).
**RxJava** est une librairie pour faire de la programmation réactive, en d'autres termes, elle permet de gérer des évènements asynchrones. C'est un paradigme puissant et prometteur bien qu'il puisse être déroutant du fait de ses différences. Nous vous recommandons de faire très attention avant d'utiliser cette librairie pour réaliser l'architecture de votre application. Parmi nous projets, certains ont été réalisés avec RxJava. Si vous avez besoin d'aide adressez vous à l'une de ces personnes : Timo Tuominen, Olli Salonen, Andre Medeiros, Mark Voit, Antti Lammi, Vera Izrailit, Juha Ristolainen. Nous avons écris des articles dessus : [[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).
Si vous n'avez aucune expérience antérieure avec Rx, commencez par mettre en oeuvre cette librairie uniquement sur les réponses des API. Une autre alternative serait de l'utiliser pour les évènements simples liés à l'interface graphique comme les clics ou l'ajout de caractères dans un champ de recherche. Si vous êtes confiants à propos de vos compétences en Rx et que vous souhaitez l'appliquer à l'architecture de votre projet, n'oubliez pas d'écrire des Javadocs au niveau de toutes les parties difficiles. Gardez en tête qu'un autre développeur qui ne connait pas RxJava aura probablement beaucoup de mal à maintenir votre projet. Faites de votre mieux pour les aider à comprendre votre code et Rx.
**[Retrolambda](https://github.com/evant/gradle-retrolambda)** est une librairie Java faite pour utiliser la syntaxe Lambda avec Android et d'autre plateformes antérieures au JDK8. Elle vous permet de garder votre code concis et lisible surtout si vous utilisez un style de programmation fonctionnel avec par exemple RxJava. Pour l'utiliser, installez le JDK8, le mettre en tant que SDK par défaut dans votre projet Android Studio et ajoutez les variables d'environnement `JAVA8_HOME` et `JAVA7_HOME`. Enfin ajout à votre build.gradle :
```groovy
dependencies {
classpath 'me.tatarka:gradle-retrolambda:2.4.1'
}
```
et dans chacun de vos fichiers build.gradle ajoutez :
```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 propose un support pour l'utilisation de lambdas Java8. Si vous débutez avec les lambdas, suivez ces conseils pour démarrer :
- Toute interface qui comporte uniquement une méthode est compatible lambda et peut être réduite dans la syntaxe la plus compacte.
- Si vous avez des doutes concernant les paramètres ou autre, écrivez une classe interne normale et laissez Android Studio la compacter en un lambda pour vous.
**Faites attention à la limiation des méthodes dex et évitez d'utiliser beaucoup de bibliothèques.** Lorsque les applications Android sont packagées en un fichier Dex, elles ont une limitation de 65536 méthodes référencées [[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/). Vous verrez une erreur fatale si jamais vous dépassez cette limite. Pour cette raison utilisez un minimum de bibliothèques et utilisez l'outil [dex-method-counts](https://github.com/mihaip/dex-method-counts) pour déterminer quelles bibliothèques peuvent être utilisées pour rester en dessous de cette limite. Evitez d'utiliser la librairie Guava car elle contient plus de 13k méthodes.
### Les activités et les fragments
Il n'y a pas d'accord global sur la meilleure façon d'oganiser une architecture Android avec des activités et des fragments ni au sein de la communauté Android ni au sein des développeurs Futurice. Square a même [une librairie pour contruire une architecture à partir de Views principalement](https://github.com/square/mortar), ce qui supprime le besoin d'utiliser des fragments. Cela n'est cependant pas considéré comme une pratique largement recommandable par la communauté Android.
De part l'histoire de l'API Android, vous pouvez considérer les Fragments comme des petites parties graphiques de l'écran. En d'autres termes, les Fragments sont en temps normal liés à l'interface graphique. Les Activities peuvent être considérées comme des controlleurs, elles sont particulièrement importante grâce à leurs cycles de vie et pour gérer les changements d'états. Vous allez cependant pouvoir constater certaines variations dans ces rôles : les activités peuvent être liées à l'interface graphique ([delivering transitions between screens](https://developer.android.com/about/versions/lollipop.html)), et [les fragments peuvent prendre le rôle d'un controlleur](http://developer.android.com/guide/components/fragments.html#AddingWithoutUI). Nous vous suggérons d'agir consciencieusement de vous informer avant de prendre des décisions car il y a des avantages et des inconvénients à chaque méthode. Voici quelques conseils à prendre avec des pincettes sur lesquels il faut rester vigilant :
- Evitez de [mettre trop de fragments les uns dans les autres](https://developer.android.com/about/versions/android-4.2.html#NestedFragments) à cause du [bug matryoshka](http://delyan.me/android-s-matryoshka-problem/). Utilisez donc des fragments à l'intérieur d'autres lorsque cela a du sens (par exemple des fragments dans un ViewPager horizontal) ou lorsque vous savez ce que vous faites.
- Evitez de mettre trop de code dans les activités. Lorsque cela est possible, laissez les aussi légers que possible en tant que conteneurs et utilisez les majoritairement pour gérer les cycles de vie ou d'autres évènement importants liés à l'API android. Préférez utiliser des fragments seuls plutôt que des activités seules - mettez le code lié à l'interface graphique dans l'activité du fragment. Cela vous permet de le rendre réutilisable dans le cas ou vous souhaiteriez le mettre dans une interface avec des onglets par exemple ou dans une interface contenant plusieurs fragments sur une tablette. Evitez d'avoir des activités sans un fragment qui leur correspond sauf si vous savez ce que vous faites.
- N'abusez pas sur l'utilisation des API android comme les Intent pour le fonctionement interne de votre application. Cela pourrait avoir de facheuses conséquences comme des bugs ou des lags. Par exemple, il a été prouvé que l'utilisation des Intent pour communiquer entre les packages de votre application peut créer des lags de plusieurs secondes si votre application a été ouverte juste après le démarrage du téléphone.
### L'architecture des packages java
L'architecture java des applications android peut être vue approximativement comme du [Model-Vue-Controlleur](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller). Avec Android, [les fragments et les activités sont des controlleurs](http://www.informit.com/articles/article.aspx?p=2126865). D'un autre côté, ils sont une partie intégrante de l'interface utilisateur, ceux sont donc aussi des vues.
C'est pour cette raison qu'il est dur de classifier les fragments (ou les activités) comme uniquement des controlleurs ou des vues. Il est préférable de les laisser dans leur package `fragments`. Les activités peuvent rester dans le package de plus au niveau tant que vous suivez les conseils de la section précédente. Si vous comptez utiliser plus de 2 ou 3 activités, faites un package `activities`.
Autrement, l'architecture ressemble à celle d'un MVC typique avec un package `models` contenant les objects POJOs à être remplis à partir des réponses des API et à l'aide d'un parser JSON. Mettre aussi un package `views` contenant toutes vos vues customisées, les notifications, les bar d'actions, les widgets, etc. Les Adapters sont une autre paire de manche, car ils se situent entre les data et les vues. Cependant ils ont besoin d'exporter une vue via la méthode `getView()` donc vous pouvez inclure les Adapters dans un sous package `adapters` dans le package `views`.
Certains controlleurs ont une porté au niveau de l'application et sont proche du système Android. Ceux-ci peuvent être placés dans le package `managers`. D'autres classes diverses manipulant des données comme "DateUtils" peuvent être placées dans le package `utils`. Les classes qui s'occupent d'interagir avec le backend restent dans le package `network`.
Pour résumer, ordonés du plus proche du back au plus proche de l'utilisateur :
```
com.futurice.project
├─ network
├─ models
├─ managers
├─ utils
├─ fragments
└─ views
├─ adapters
├─ actionbar
├─ widgets
└─ notifications
```
### Ressources
**Règles de nommage.** Suivez les conventions en préfixant vos noms du type comme `type_foo_bar.xml`. Exemples: `fragment_contact_details.xml`, `view_primary_button.xml`, `activity_main.xml`.
**Organisation des fichiers XMLs.** Si vous n'êtes pas sûr(e) de l'organisation de vos fichiers XMLs, les conventions suivantes pourraient vous aider :
- Un attribut par ligne, indenté d'espaces
- `android:id` toujours en tant que premier attribut
- les `android:layout_****` toujours en haut après `android:id`
- l'attribut `style` tout en bas
- le tag `/>` de fermeture doit être sur sa propre ligne pour faciliter l'ajout d'attributs et l'ordonnancement.
- plutôt que de hardcoder l'attribut `android:text`, pensez à utiliser les [attributs Designtime](http://tools.android.com/tips/layout-designtime-attributes) disponibles dans Android Studio.
```xml
<?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>
```
En règle générale, les attributs `android:layout_****` doivent être placés dans les fichiers layouts XML et les autres attributs `android:****` dans les fichiers de style XML. Cette règle a des exceptions mais s'applique bien la plupart du temps. L'idée est de garder uniquement les attributs de contenu et de layout (positionnement, marges, tailles) dans les fichiers de layout XML et de mettre tous les autres liés aux détails de l'apparence (couleurs, padding, police) dans des fichiers de style.
Les exceptions sont :
- `android:id` doit évidement se trouver dans les fichiers layout XML
- l'attribut `android:orientation` pour un `LinearLayout` a normalement plus de sens dans les fichiers layout XML
- l'attribut `android:text` devrait être dans les fichiers layout XML car il définit du contenu
- De temps en temps il est judicieux de mettre un style générique pour définir `android:layout_width` et `android:layout_height` mais par défaut ils devraient apparaitre dans les fichiers de layout XML.
**Utilisez des styles.** Quasiment tous les projets ont besoin d'utiliser correctement des styles, car il est très commun d'avoir des vues dont l'apparence est similaire. Vous devriez au moins avoir un style commun pour la plupart des contenu textuels dans votre application, par exemple :
```xml
<style name="ContentText">
<item name="android:textSize">@dimen/font_normal</item>
<item name="android:textColor">@color/basic_black</item>
</style>
```
Appliqué aux TextViews:
```xml
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/price"
style="@style/ContentText"
/>
```
Vous aurez probablement besoin de faire la même chose pour les boutons, mais ne vous arretez pas là. Allez plus loin et mettez les groupes d'attributs qui se répètent dans un style commun.
**Séparez les fichiers de style très gros en plusieurs petits fichiers de style.** Vous n'êtes pas contraints d'avoir qu'un seul fichier `styles.xml`. Le SDK Android supporte plusieurs fichiers de style, il n'y a rien de magique avec le nom `styles`, ce qui importe ce sont les tags XML `<style>` à l'intérieur du fichier. Donc vous pouvez très bien avoir les fichiers `styles.xml`, `styles_home.xml`, `styles_item_details.xml`, `styles_forms.xml`. A l'opposé des fichiers dans le dossier de ressources qui doivent être nommés précisément pour que le système de build Android puisse les comprendre, les noms de fichier dans `res/values` sont arbitraires.
**`colors.xml` est une palette de couleurs.** Il ne devrait avoir rien d'autre à part des correspondances entre un nom de couleur et une valeur RGBA dans le fichier `colors.xml`. N'utilisez pas ce fichier pour définir différents types de boutons.
*Ne faites pas ça:*
```xml
<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>
```
Avec ce format il est facile de devoir se répéter et il est difficile de changer une couleur de base si besoin. De plus ces définitions sont liés à un contexte, comme "button" ou "comment" et devrait être placés dans un style de bouton et non dans le fichier `colors.xml`.
A la place, faites ceci:
```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>
```
Demandez cette palette de couleur à votre designer. Les noms n'ont pas besoin d'être des noms de couleur comme "vert", "bleu", etc. Les noms comme "brand_primary", "brand_secondary", "brand_negative" sont tout à fait acceptables. Le fait d'arranger les couleurs comme ceci vous permettra de les changer facilement et permettra de voir explicitement le nombre de couleurs différentes utilisées dans votre application. Normalement pour une interface graphique agréable à regarder, il faut minimiser le nombre de couleurs différentes utilisées.
**Traitez dimens.xml comme colors.xml.** Vous devriez aussi définir une "palette" de dimensions, d'espacements et de tailles de police typiques à votre application. Un bon exemple de fichier dimens.xml est :
```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>
```
Vous devriez utiliser les dimensions `spacing_****` pour le layout, les marges et les padding au lieu d'harcoder les valeurs. Il s'agit de traiter les dimensions comme les strings. Cela donnera à votre application de la consistence au niveau de l'apparence tout en facilitant l'organisation des styles et des layouts.
**strings.xml**
Nommez vos strings avec des clés qui ressemblent aux espaces de noms et n'ayez pas peur de répéter les valeurs pour deux ou plusieurs clés car les espaces de noms sont nécessaire pour apporter un contexte et enlever toute ambiguité.
**Pas bien**
```xml
<string name="network_error">Erreur de réseau</string>
<string name="call_failed">L'appel a échoué</string>
<string name="map_failed">Le chargement de la carte a échoué</string>
```
**Bien**
```xml
<string name="error.message.network">Erreur de réseau</string>
<string name="error.message.call">L'appel a échoué</string>
<string name="error.message.map">Le chargement de la carte a échoué</string>
```
N'écrivez pas les strings en majuscules. Basez vous sur les conventions des textes (ex.: en mettant une majuscule à la première lettre). Si vous avez besoin d'afficher le texte tout en majuscules, faites le en utilisant l'attribut [`textAllCaps`](http://developer.android.com/reference/android/widget/TextView.html#attr_android:textAllCaps) dans une TextView.
**Pas bien**
```xml
<string name="error.message.call">L'APPEL A ECHOUE</string>
```
**Bien**
```xml
<string name="error.message.call">L'appel a échoué</string>
```
**Evitez d'avoir une hiérarchie trop profonde de vues.** De temps à autres vous serez tenté d'ajouter simplement un autre LinearLayout pour accomplir l'arrangement des vues que vous souahité. Ce genre de situation peut arriver :
```xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<RelativeLayout
...
>
<LinearLayout
...
>
<LinearLayout
...
>
<LinearLayout
...
>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
```
Même si vous ne voyez pas explicitement ceci dans un fichier de layout, cela arrivera peut être lorsque vous ajouterez des vues (en Java) dans d'autres vues.
Plusieurs problèmes pourront alors survenir. Vous pourrez par exemple rencontrer des problèmes de performances parce que le processeur doit gérer une arborescence de vues complexe. Une autre erreur aux conséquences grave serait : [StackOverflowError](http://stackoverflow.com/questions/2762924/java-lang-stackoverflow-error-suspected-too-many-views).
Essayez donc de garder vos vues aussi plates que possible: apprenez à utiliser le [RelativeLayout](https://developer.android.com/guide/topics/ui/layout/relative.html), comment [optimize your layouts](http://developer.android.com/training/improving-layouts/optimizing-layout.html) et comment utiliser [`<merge>` tag](http://stackoverflow.com/questions/8834898/what-is-the-purpose-of-androids-merge-tag-in-xml-layouts).
**Faites attention aux problèmes liés aux WebViews.** Lorsque vous devez afficher une page web, par exemple pour un article de presse, évitez de faire des manipulations sur la page du coté client, demandez plutôt aux développeurs backend de vous fournir une version "*pure*" de leur page HTML. [Les WebViews peuvent aussi provoquer des fuites de mémoire](http://stackoverflow.com/questions/3130654/memory-leak-in-webview) lorsqu'elles gardent en référence leur activité à la place d'être référencées à l'ApplicationContext. Evitez d'utiliser les WebViews pour afficher des simple textes ou des boutons, préferez des TextViews ou des Buttons.
### Emulateurs
Si votre métier est de développer des applications Android, achetez une license de [l'émulateur Genymotion](http://www.genymotion.com/). Les émulateurs Genymotion ont un taux de rafraichissement plus rapide que les machines virtuelles AVD. Ils ont aussi des outils qui permettent de faire des démos de votre application, de simuler tel ou tel type de connection internet, de simuler des positions GPS, etc. Ils sont aussi idéals pour faire des tests connectés. Vous aurez accès à beaucoup de différents périphériques (pas tous mais beaucoup) donc le coup d'achat d'une license de Genymotion revient moins cher qu'acheter le nombre équivalent de périphériques.
Les inconvénients sont : les émulateurs Genymotion ne contiennent pas tous les services Google comme Google Play Store ou Maps. Vous aurez aussi probablement besoin de tester des APIs spécifiques aux téléphones samsung donc vous aurez besoin d'acheter un vrai téléphone pour ça.
### Configuration Proguard
[ProGuard](http://proguard.sourceforge.net/) est utiliser en temps normal pour diminuer la taille des applications Android et pour obfuscer le code packagé.
<!-- Le fait d'utiliser ou nom Proguard dépend de la configuration de votre projet. Normalement on configure gradle pour utiliser Proguard lorsqu'on build la version release de l'apk. -->
```groovy
buildTypes {
debug {
minifyEnabled false
}
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
```
Pour déterminer quels bouts de code doivent être gardés tel quel et lesquels doivent ête supprimés ou obfusqués, vous devez spécifier un ou plusieurs points d'entrée dans votre code.
La configuration par défaut du framework Android se trouve ici : `SDK_HOME/tools/proguard/proguard-android.txt`. Les règles spécifiques à chaque projet seront ajoutées automatiquement à la configuration par défaut à partir du fichier `my-project/app/proguard-rules.pro`.
Un problème récurrent lié à l'utilisation de Proguard est le crash de l'application à son démarrage avec `ClassNotFoundException` ou `NoSuchFieldException` ou autre erreur similaire même si la commande de build a réussi (i.e.: `assembleRelease`) sans avertissements.
Cela peut dire deux choses :
1. ProGuard a supprimé la class, l'enum, la méthode, le champ ou l'annotation en pensant qu'il/qu'elle n'était pas nécessaire.
2. ProGuard a obfusqué (renommé) la classe, l'enum ou le champ alors qu'il/qu'elle est utilisé indirectement avec son nom d'origine, i.e. à travers la réflexion Java.
Vérifiez `app/build/outputs/proguard/release/usage.txt` pour voir si l'objet en question a été supprimé.
Vérifiez `app/build/outputs/proguard/release/mapping.txt` pour voir si l'objet en question a été obfusqué (renommé).
Afin d'empêcher ProGuard de *supprimer* les classes qui sont nécessaires ou les champs qui sont nécessaires au bon fonctionement de l'application, ajoutez l'option `keep` à votre configuration ProGuard :
```
-keep class com.futurice.project.MyClass { *; }
```
Pour empêcher ProGuard *d'obfusquer* les classes ou les champs des calsses, ajoutez l'option `keepnames`:
```
-keepnames class com.futurice.project.MyClass { *; }
```
Lisez [Proguard](http://proguard.sourceforge.net/#manual/examples.html) pour obtenir plus d'exemples.
**Faites un build de la release tôt dans le développement de votre application** pour vérifier si ProGuard garde correctement ce qui est important. De plus dès que vous ajoutez une nouvelle librairie dans votre projet, faites une build de la release et testez l'apk sur votre périphérique. N'attendez pas que votre application soit finalisée en version "1.0" pour faire ce build de la release car vous risquez de tomber sur de mauvaises surprises et d'avoir peu de temps pour les corriger.
**Conseil.** Sauvegardez le fichier `mapping.txt` pour chaque release que vous publiez aux utilisateurs. En gardant une copie de ce fichier pour chaque build de release, vous vous assurez de pouvoir débugguer le problème si un utilisateur rencontre un bug et vous envoie le rapport avec le code obfusqué.
**DexGuard**. Si vous avez besoin d'outils surpuissants pour optimiser votre application et surtout pour obfusquer votre code, utilisez [DexGuard](http://www.saikoa.com/dexguard), un logiciel commercial fait par la même équipe que ProGuard. Ce logiciel est aussi capable de couper les fichiers Dex en plusieurs fichiers afin de contourner la limite des 65k méthodes.
### Remerciements
Antti Lammi, Joni Karppinen, Peter Tackage, Timo Tuominen, Vera Izrailit, Vihtori Mäntylä, Mark Voit, Andre Medeiros, Paul Houghton et les autres développeurs à Futurice pour le partage de leur connaissances sur le développement Android.
### License
[Futurice Oy](http://www.futurice.com)
Creative Commons Attribution 4.0 International (CC BY 4.0)
================================================
FILE: translations/Japanese/README.ja.md
================================================
# Androidの開発におけるベストプラクティス
以下は[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)を必ず確認しよう。
フィードバックは歓迎しているが、まずは[ガイドライン](https://github.com/futurice/android-best-practices/tree/master/CONTRIBUTING.md)を読んでほしい.
[](https://android-arsenal.com/details/1/1091)
## Summary
#### Gradleで推奨されるプロジェクト構成で開発しよう
#### パスワードや注意を要するデータはgradle.propertiesに書こう
#### 自分でHTTP Clientは作らず、VolleyやOkHttpを使おう
#### JSONをパースするならJacksonを使おう
#### メソッド数に65kの制限があるので、Guavaは避けて、かつライブラリは最小限に抑えよう
#### UIの描画はFragmentを使おう
#### ActivityはFragmentをただ管理するために使おう
#### Layout xmlをうまく整理しよう
#### Layout xmlの属性が重複するのを避けるためStyleを使おう
#### 大きなStyleを定義するよりも複数のStyleを定義しよう
#### colors.xmlは短くDRY(「Don't Repeat Yourself」意味が重複しないよう)にして、パレットで定義しよう
#### dimens.xmlもDRYにして、一般の定数を定義しよう
#### ViewGroupのネストは深くせずに浅くしよう
#### WebViewはメモリリークするため、クライアント側での処理は避けよう
#### ユニットテストにはRobolectricを、結合テストにはRobotiumを使おう
#### emulatorはGenymotionで決まり
#### 必ずProGuardもしくはDexGuardを使おう
----------
### Android SDK
[Android SDK](https://developer.android.com/sdk/installing/index.html?pkg=tools)はホームディレクトリか他のアプリから独立した場所に置こう。いくつかのIDEはSDKを含んでいて、インストール時にSDKをIDEと同じディレクトリに置く事がある。このままではIDEをアップグレードや再インストールする場合、またIDEを変更する場合に面倒になる。
また、IDEがrootでないアカウントで動いている場合に、sudoが必要な他のシステムレベルのディレクトリにSDKを置く事も避けよう。
### Build system
デフォルトオプションに[Gradle](http://tools.android.com/tech-docs/new-build-system)を使おう。Antは制限が多く、コストが大きい。しかし、Gradleなら下記のことがシンプルに可能だ。
- あなたのアプリの異なるFlavorやVariantをビルドできる
- スクリプトのようにタスクを作る事ができる
- 依存関係を管理しつつダウンロードできる
- keystoreをカスタマイズできる
- その他諸々
そして、Googleは、Androidの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`というSource setを`src`の中に追加すると、'paid'と'free'というFlavorができる。
さらに`app`がtop-levelにあると、アプリで参照される`library-foobar`などの他のライブラリプロジェクトを区別するのに役立つ。`setting.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などのスクリプトの代わりに、Gradleの中にタスクをつくることができる。詳細は[Gradle's documentation](http://www.gradle.org/docs/current/userguide/userguide_single.html#N10CBF)を参照の事。
**パスワード**
リリースビルドのために`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によって自動でimportされるので、このときの`build.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.")
}
}
}
```
**jarファイルを直接importするよりMavenを使う方が良い**
jarファイルをプロジェクトに直接includeしている場合、version`2.1.1`のようにversionが固定されてしまう。さらに、jarファイルをダウンロードして、手動でアップデートするはめになり効率が悪い。Mavenならこれを解決できるし、Android StudioもMevenの使用をすすめている。`2.1.+`というようにversionの範囲を指定することもでき、指定した範囲の中での最新にアップデートしてくれる。
たとえば下記のように書く。
```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.+'
}
```
### IDEとテキストエディタ
**エディターは何を使っても良いが、例のプロジェクト構成のまま扱えるものが良い**
エディターはプロジェクト構成、ビルドシステムにあったものを選べば良い。
一番推奨されるIDEは[Android Studio](https://developer.android.com/sdk/installing/studio.html)である。Google が開発しており、Gradleと親和性が高いうえ、プロジェクト構成もデフォルトで推奨されているものを採用しているからだ。
もしお望みなら[Eclipse ADT](https://developer.android.com/sdk/installing/index.html?pkg=adt)をつかってもいいが、デフォルトで古いプロジェクト構成でかつAntを採用しているので設定が必要である。EclipseのGradleプラグインが動かない場合はコマンドラインでやるか、Android Studioに移行するかしかない。
またVim、Sublime TextやEmacsといった純粋なテキストエディタを使用することさえできる。その場合は、コマンドラインからGradleとadbを使えるよう準備する必要がある。
何を使っても良いが、Gradleを使う事と新しいプロジェクト構成で開発する事がオフィシャルな方法である事を頭に入れておかねばならない。またAntの`build.xml`などのエディタ特有の設定ファイルなどはバージョン管理外とすることもお忘れなく。特に、Antのビルド設定を変更した際は`build.gradle`が最新であり機能する事を確認することを怠ってはならない。また他の開発者が使っているツールの設定を強要することがないようにしよう。
### Libraries
**[Jackson](http://wiki.fasterxml.com/JacksonHome)** はObjectをJSONに変換、またその逆を行うライブラリである。[GSON](https://code.google.com/p/google-gson/)もJsonのライブラリとして有名だが、streaming、in-memory tree model, Json data binding等をサポートしている点でJacksonの方がいくらか優れていると判断した。ただ覚えておいてほしいのはJacksonがGsonよりボリュームの大きなライブラリである事だ。65k制限を避ける為にGSONの方が有効なケースもあり得る。また他には[json-smart](https://code.google.com/p/json-smart/)、[Boon JSON](https://github.com/boonproject/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/)という画像ライブラリ、またHttpリクエストに有効な[OkHttp](http://square.github.io/okhttp/)の導入も考えると良い。この三つのRetrofit、Picasso、OkHttpは一つの会社で作られている。そのため、これらのライブラリは互いをよく補っている。現に[OkHttpは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://futurice.com/blog/tech-pick-of-the-week-rx-for-net-and-rxjava-for-android) [\[2\]](http://futurice.com/blog/top-7-tips-for-rxjava-on-android) [\[3\]](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754) [\[4\]](http://futurice.com/blog/android-development-has-its-own-swift)
もしRxを使った経験が以前にないのなら、まずはAPIのレスポンスのみ、もしくはクリックイベントや検索フィールドのテキスト変更イベントなどのUIのイベントハンドリングのみに適用するところから始めると良い。
逆にRxに自信があってプロジェクト全体で使いたい場合は、トリッキーな場所にJavadocを書くと良い。RxJavaのようなよく知られていない他のプログラミング手法を使用する場合はメンテナンスが大変であることを忘れてはいけない。あなたのRxで書かれたコードが他の人も理解できるようにベストを尽くそう。
**[Retrolambda](https://github.com/evant/gradle-retrolambda)**はAndroidやJDK8以前のプラットフォームでlambda記法を使う事ができるようになるライブラリである。特にRxJavaなど関数型スタイルを採用する場合において、これはコードを短く、見やすくする。
JDK8をインストールして、SDKのときと同じようにAndroid Studioに設定する必要がある。
`JAVA8_HOME`と`JAVA7_HOME`を設定後、ルートのbuild.gradleを下記のように書き
```groovy
dependencies {
classpath 'me.tatarka:gradle-retrolambda:2.4.+'
}
```
そしてそれぞれのモジュールを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のlambda記法のためのコードアシストをサポートしている。もし、あなたがlambdaのビギナーであれば、下記にならって使ってみよう
- 一つだけメソッドがあるインターフェイスはどれも“lambdaに親和性あり“で、構文をより短くできる
- もしパラメータなどに迷ったときは、普通のインナークラスを書いて、Android Studioでそれをlambdaに盛り込ませてみよう
**65k制限に注意して、たくさんのライブラリを使うのを避けよう。**
Androidアプリはdexファイルにパッケージングする際に関数の参照は65536個までという厳格な制限がある。[\[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/)。制限を超えた場合はFatal Errorが起きる、そのため、ライブラリは最小限とし、制限内に収まるよう[dex-method-counts](https://github.com/mihaip/dex-method-counts)ツールを使って使用するライブラリを決めよう。特にGuavaは13kも関数があるので避けた方がいい。
### Activities and Fragments
AndroidにおいてはUIの実装は[Fragments](http://developer.android.com/guide/components/fragments.html)で行うべきである。Fragmentsは、アプリ内で構成出来る再利用可能なUIである。ActiivtyではUIの実装をせずにFragmentsで行うことをすすめる。理由は下記である。
- **multi-pane layoutの解決について。** Fragmentは元はphoneアプリケーションをTableアプリケーションへ拡張するためのものとして紹介された。phoneアプリでAもしくはBというpaneが占めている場合に、TabletではAとBを表示することができる。はじめからfragmentを使っているなら、あとで異なるフォームファクタへ変更する事が簡単になる。
- **スクリーン間のコミニケーションについて。** AndroidはActivity間の通信として複雑なデータ(たとえばJava Object)を送るAPIを提供していない。Fragmentであれば、その子Fragment間の通信経路としてActivityのインスタンスを使う事ができる。とはいえ、[Otto](https://square.github.io/otto/)や[greenrobot-EventBus](https://github.com/greenrobot/EventBus)を使ってEventBusを使いたいと思うだろう。他のライブラリの追加を避けたい場合はRxJavaを用いてEventBusを実装する事も可能である。
- **FragmentはUIだけじゃなくても十分につかえる。** ActivityのbackgroundワーカとしてfragmentをUIなしでもつこともできる。Activityにロジックを持たせる代わりに、[複数のfragmentを変更するロジックを持ったfragmentを作ることも可能である](http://stackoverflow.com/questions/12363790/how-many-activities-vs-fragments/12528434#12528434)。
- **FragmentからActionBarを管理できる。** ActionBarを管理するだけのUIを持っていないFragmentをもっても良いし、また現在表示されているFragmentにActionBarの振る舞いをまかせても良い。詳しくは[こちら](http://www.grokkingandroid.com/adding-action-items-from-within-fragments/)を参考にしてほしい。
[matryoshka bugs](http://delyan.me/android-s-matryoshka-problem/)が起きるので、[Fragmentを大規模にネスト](https://developer.android.com/about/versions/android-4.2.html#NestedFragments)すべきではない。例えば、横スライドするViewPagerをもったfragmentのなかに表示するfragmentといった理にかなったケースもしくは十分に考えられている場合のみ使用すべきである。
設計レベルの話をすると、アプリは一つのトップレベルの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))などで簡単に遷移でき、メインのActivityをサポートするくらいにすると良い。
### Java package 構成
Androidにおける設計はおおよそ[Model-View-controller](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)である。[Androidにおいては、FragmentとActivityがControllerに相当する](http://www.informit.com/articles/article.aspx?p=2126865)が、一方で、これらは明らかにユーザインターフェイスであり、Viewでもある。
以上のことから、FragmentまたはActivityを厳格にControllerかViewかに分類する事は難しい。そのため分類せずにfragmentは`fragments`というパッケージに納めると良い。Activityはトップレベルのパッケージに置いても良いが、もし2つ3つ以上あるなら`activities`というパッケージを作ると良い。
その他は典型的なMVCだ。`models`パッケージはAPIレスポンスをJSONパースした結果のPOJOを入れ、`views`にはカスタムView,Notifications,ActionBar View, ウィジェットなどを配置する。Adapterはviewとデータの間にいるので微妙だが、`getView()`メソッドからViewを生成する必要がよくあるので、`views`パッケージのサブパッケージとして`adapters`を作りそこに配置すると良い。
アプリ内で多様に使われAndroidシステムに密接なコントローラクラスは`managers`パッケージへ、各Dataを混ぜこぜにつかうDataUtilのようなクラスは`utils`へ、バックエンドとインタラクティブに反応するクラスは`network`パッケージに配置しよう。
バックエンドに最も近いところからユーザに最も近いところへという順で並べると下記のような構成になる。
```
com.futurice.project
├─ network
├─ models
├─ managers
├─ utils
├─ fragments
└─ views
├─ adapters
├─ actionbar
├─ widgets
└─ notifications
```
### Resources
**命名規則について。**
たとえば、`fragment_contact_details.xml`, `view_primary_button.xml`, `activity_main.xml`といったように、`type_foo_bar.xml`というタイプを接頭辞とする慣習に従おう。
**Layout xmlを整理しよう。**
もしLayout xmlのフォーマット方法が不明確なら下記の慣習が役立つと思われる。
- 1 attribute につき 1 lineでインデントは4スペース
- `android:id`は常に一番始めに
- `android:layout_****`は上に
- `style`は一番下
- attributeを追加しやすいように`/>`のみで1 line
- `android:text`をハードコーディングするよりは、[Designtime attributes](http://tools.android.com/tips/layout-designtime-attributes)を使う事を考えた方が良い
```xml
<?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:****`はStyle xmlの中で定義すると良い。下記を除くと大抵この方法でうまくいく。
- `android:id`は確実にLayout xml内に
- LinearLayoutの'android:orientation`はLayout xml内に
- `android:text`はLayout xmlに
- 時々Style xmlに`android:layout_width`と`android:layout_height`を定義してうまく行く事がある。(しかし、デフォルトではこれらはlayout filesの中にある)
**Styleを使おう。**
Viewに統一感を持たせる為にStyleを適切に使う必要がある。Viewが繰り返し出ることはよくあることだからだ。少なくとも下記のようにText用のStyleは持っておいた方が良い。
```xml
<style name="ContentText">
<item name="android:textSize">@dimen/font_normal</item>
<item name="android:textColor">@color/basic_black</item>
</style>
```
このスタイルはTextViewで下記の用に使う事ができる。
```xml
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/price"
style="@style/ContentText"
/>
```
同じことをボタンにもする必要があるが、そこで終わりにせず、関連性のあるまたは繰り返されたグループを移動して、`android:****`を共通のStyleに書き出そう。
**大きなStyle Fileを避け、複数に分けよう。**
1つの`styles.xml`だけを持つ事は止めた方が良い。styleファイルは`style_home.xml`、`style_item_details.xml`、`styles_forms.xml`と言ったように複数持つ事ができる。`res/values`の中のファイル名は任意である。
**`color.xml`はカラーパレットである。**
colors.xmlは色の名前で定義しよう。下記のように各ボタンによって定義するといったようなことはすべきじゃない。
*下記は良くない例。*
```xml
<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>
```
こういう書き方をしてしまうと基本色を変更する場合などに対応しづらい。"button"や"comment"といった内容はbutton styleで定義すればよく、`colors.xml`の中に定義すべきではない。
`colors.xml`は下記のように定義しよう。
```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>
```
nameは色の名前でなく"brand_primary", "brand_secondary", "brand_negative"などとしても良い。そうする事で色の変更がしやすくなり、またどれだけの色が使われているかがわかりやすい。通常、きれいなUIの為には使用する色を減らす事も重要だ。
**dimens.xmlもcolors.xmlのように扱おう。**
典型的なスペースやフォントサイズをcolors.xmlのパレットのように定義しよう。下記は良い例である。
```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>
```
marginやpaddingをハードコードするのではなく、`spacing_****`を使用するようにしよう。そうする事で簡単に全体に統一感を持たす事ができ、また整理も簡単にできる。
**Viewの深いネストは止めよう。**
下記のようにLinearLayoutを組み合わせてViewを作ろうとすることがある。
そうするとLayout xmlは下記のようになる。
```xml
<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側でinflateした際に同じ状況になる事もあり得る。
これはいくつかの問題起こす。まずはきっとあなたも経験しただろう、UIの煩雑さによるパフォーマンス低下の問題である。他にも深刻な[StackOverFlow](http://stackoverflow.com/questions/2762924/java-lang-stackoverflow-error-suspected-too-many-views)を起こす可能性もある。
以上の理由から、Viewの階層はなるべくフラットにするべきである。そのために[RelativeLayout](https://developer.android.com/guide/topics/ui/layout/relative.html)の使い方、[Layoutの最適化](http://developer.android.com/training/improving-layouts/optimizing-layout.html)の方法、[\<merge\>タグ](http://stackoverflow.com/questions/8834898/what-is-the-purpose-of-androids-merge-tag-in-xml-layouts)の使い方を知っておこう。
**WebViewの参照問題に気をつけよう。**
例えばNewsの記事などのweb pageを表示する必要がある場合、クライアントサイドでHTMLを整形する事は止めた方が良い。HTMLはバックグラウンドプログラマに用意してもらおう。また[WebViewはActivityの参照を持つときにメモリリークしうる](http://stackoverflow.com/questions/3130654/memory-leak-in-webview)。Activityの代わりにApplicationContextを使用しよう。単純なテキストやボタンを表示するのにTextViewやButtonではなくWebViewを使用する事も避けた方がいい。
### テストフレームワーク
Android SDKのテストフレームワークは特にUIテストにおいてまだまだ未熟なところがある。
Android Gradleに、あなたがAndroidのJUnitのヘルパーを使って書いたJUnitのテストを走らせる[connectedAndroidTest](http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Testing)がある。これはdeviceもしくはemulatorをつなぐ必要がある。次のテストガイドを見ておこう。[\[1\]](http://developer.android.com/tools/testing/testing_android.html) [\[2\]] (http://developer.android.com/tools/testing/activity_test.html)
**viewを使わないUnitテストには[Robolectric](http://robolectric.org/)を使おう。**
このテストフレームワークはデバイスにつなぐ必要がないため開発効率があがる。UIのテストには向いていないがモデルとViewモデルのテストに適している。
**[Robotium](https://code.google.com/p/robotium/)は簡単にUIテストを作る事ができる。**
このテストフレームワークにはViewの取得、解析する為のヘルパーメソッド、スクリーンをコントロールする為のヘルパーメソッドが多く用意されている。テストケースも下記のようにシンプルに書く事ができる。
```java
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"));
```
### Emulator
Androidアプリを専門で開発していくなら[Genymotion emulator](http://www.genymotion.com/)のライセンスは買っておいた方が良い。Genymotionは通常のAVD Emulatorよりも早い。またアプリのデモ、ネットワーク接続品質のエミュレート、GPS、などなどを行う為のツールがそろっている。
テストはたくさんの端末で行わなければならないが、実際にたくさんの端末を買うよりはGenymotionのライセンスを買う方がコスパが良い。
注: GenymotionはGoogle Play StoreやMapなどがインストールされていない。またSamsungの特定のAPIをテストしたい場合は、実際のSamsungの端末を使う必要がある。
### Proguardの設定
[ProGuard](http://proguard.sourceforge.net/)はAndroid Projectでコードを圧縮、難読化するために使われる。
Proguardを使用するか否かはプロジェクトの設定に依る。通常リリースapkをビルドする際にProguardを使う場合gradleを下記のように設定する。
```groovy
buildTypes {
debug {
runProguard false
}
release {
signingConfig signingConfigs.release
runProguard true
proguardFiles 'proguard-rules.pro'
}
}
```
どのコードを保存し、どのコードを捨て難読化するかをを明確に示さなければならない。デフォルトの設定では`SDK_HOME/tools/proguard/proguard-android.txt`を使用する。また`my-project/app/proguard-rules.pro`に定義する事でデフォルトのものに追加することができる。
ProGuard関連のよくある問題でビルドが成功したにもかかわらず、アプリの起動で`ClassNotFoundException`や`NoSuchFieldException`などのExceptionを発生してアプリがクラッシュすることがある。これは以下の二つのどちらかを意味する。
1. ProGuardが必要なクラス、enum、関数、フィールド、アノテーションを削除してしまった。
2. リフレクションなどを使っており難読化によってリネームされたクラスへの参照ができない。
もしあるオブジェクトが削除されている疑いがあるなら`app/build/outputs/proguard/release/usage.txt`を確認しよう。オブジェクトの難読化結果を見るなら`app/build/outputs/proguard/release/mapping.txt`を確認しよう。
必要なクラスや関数の削除を防ぐには`keep`オプションをproguard configに追加しよう。
```
-keep class com.futurice.project.MyClass { *; }
```
難読化を防ぐには`keepnames`を使う。
```
-keepnames class com.futurice.project.MyClass { *; }
```
**Tip**
リリースするたびに`mapping.txt`を保存しておこう。ユーザがバグを踏み、難読化されたスタックトレースを送ってきた際にデバッグする事ができる。
**DexGuard**
さらに最適化され、さらに難読化されたものを考えるならDexGuardの採用を考えよう。DexGuardはProGuardを作った同じチームが開発している商用のものである。さらにDexGuardなら簡単にDexファイルを分割し65k制限を解決する。
### 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 Oy](www.futurice.com)
Creative Commons Attribution 4.0 International (CC BY 4.0)
Translation
===========
Translated to Japanese (`ja`) by **Shinnosuke Kugimiya, Aska Kadowaki**.
Original content by [Futurice Oy](http://www.futurice.com).
================================================
FILE: translations/Korean/README.ko.md
================================================
# Android 개발 모범 사례
다음은 [Futurice](http://www.futurice.com)의 Android 개발자들로부터 학습한 내용들이다. 이 가이드를 따라가면서 이미 했던 작업을 되풀이(Reinventing the wheel)하지 않도록 하자. iOS나 Windows Phone 개발에도 관심이 있다면, [**iOS Good Practices**](https://github.com/futurice/ios-good-practices) 혹은 [**Windows App Development Best Practices**](https://github.com/futurice/windows-app-development-best-practices) 문서들도 확인해보자.
[](https://android-arsenal.com/details/1/1091)
## 요약
#### Gradle을 사용하자. 이는 권장되는 프로젝트 구조이다.
#### gradle.properties에 비밀번호나 민감한 데이터들을 넣어두자.
#### HTTP 클라이언트를 직접 작성하지 말고, Volley나 OkHttp 라이브러리들을 사용하자.
#### JSON 데이터를 파싱하는 데에는 Jackson 라이브러리를 사용하자.
#### 65,000 메소드 수 제한을 방지하기 위해 Guava는 피하고 몇 가지의 라이브러리들만을 사용하자.
#### UI 화면을 표현하는 데에 Fragment들을 사용하자.
#### Fragment들을 관리하는 것은 Activity들이 맡도록 하자.
#### 레이아웃 XML들 또한 코드이다. 그 것들을 잘 관리하자.
#### 레이아웃 XML에서 중복된 속성들을 피하기 위해 style을 사용하자.
#### 하나의 style이 방대해지는 것을 피하기 위해 여러가지 style 파일들을 사용하자.
#### colors.xml을 짧게, 중복 없이 유지하고, 팔렛트처럼 정의해두자.
#### dimens.xml 또한 중복 없이, 일반 상수로 정의하자.
#### ViewGroup에 계층을 깊게 형성하지 않도록 하자.
#### WebView에 클라이언트 측 프로세싱을 피하고, 여러 누수들에 유의하자.
#### 유닛 테스트에는 Robolectric를 사용하고, UI 테스트에는 Robotium을 사용하자.
#### 에뮬레이터로는 Genymotion를 사용하자.
#### 항상 ProGuard 혹은 DexGuard를 사용하자.
----------
### Android SDK
[Android SDK](https://developer.android.com/sdk/installing/index.html?pkg=tools)를 홈 디렉토리나 다른 애플리케이션에 독립적인 위치에 두자. 몇몇 IDE들은 설치시에 SDK를 해당 IDE와 같은 경로에 포함시키는데, 이는 IDE를 업그레이드(혹은 재설치)하거나 IDE가 변경될 때 불편하다. 또한 IDE가 root 아래에 있지 않고 user 아래에서 동작할 경우, SDK를 sudo 권한을 요구하는 시스템 레벨의 디렉토리에 두지 않도록 하자.
### 빌드 시스템
기본 옵션은 [Gradle](http://tools.android.com/tech-docs/new-build-system)이다. Ant는 상당히 제한적이고 내용이 장황하다. Gradle을 사용하면, 다음 항목들이 간단해진다.
- 앱의 각기 다른 Flavor들과 Varient들을 빌드할 수 있다.
- Task들을 간단한 스크립트처럼 만들 수 있다.
- 여러 Dependency들을 관리하고 다운로드할 수 있다.
- Keystore들을 커스터마이즈할 수 있다.
- 기타 등등
Android의 Gradle 플러그인은 새로운 표준 빌드 시스템으로서 구글에 의해 활발하게 개발되고 있다.
### 프로젝트 구조
두 가지 많이 쓰이는 옵션들이 있다: 낡은 Ant & Eclipse ADT 프로젝트 구조, 새로운 Gradle & Android Studio 프로젝트 구조가 있는데, 새로운 프로젝트 구조를 선택하자. 만약 낡은 구조를 사용하고 있다면, 레거시로 판단하고 새로운 구조로 포팅하는 작업을 시작하자.
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`)를 명시적으로 분리시켜둔다는 것이다. 예를 들어 `src`에 paid와 free라는 각기 다른 Flavor에 해당하는 소스코드를 갖는 'paid'라는 소스 셋과 'free'라는 소스 셋을 추가할 수 있다.
최상위 레벨 `app`을 갖는 것은 앱과 앱에서 참조된 다른 라이브러리 프로젝트들(e.g., `library-foobar`)을 구별하는 데에 유용하다. `settings.gradle`은 `app/build.gradle`에서 참조할 수 있는 이러한 라이브러리 프로젝트들을 보관한다.
### Gradle 설정
**일반적인 구조.** [Google's guide on Gradle for Android](http://tools.android.com/tech-docs/new-build-system/user-guide)를 확인하자.
**작은 Task들.** 스크립트들(shell, Python, Perl, etc) 대신, Gradle의 Task들을 만들 수 있다. [Gradle's documentation](http://www.gradle.org/docs/current/userguide/userguide_single.html#N10CBF)에서 더 자세한 내용을 확인하자.
**비밀번호** 앱의 `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에 자동으로 임포트되어, `build.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.")
}
}
}
```
**jar 파일 임포트 대신 Maven을 선호하자.** 프로젝트에 jar 파일을 명시적으로 포함시킬 경우, 이들은 `2.1.1`처럼 특정하게 고정된 버전이 된다. jar를 다운로드하고, 업데이트하는 것은 귀찮은 일이다. 그러나 Maven은 이 문제를 적절하게 해결해줄 것이고, 또한 이는 Android Gradle 빌드에서 장려되는 방식이다. 예를 들자면 이렇다:
```groovy
dependencies {
implementation 'com.squareup.okhttp:okhttp:2.2.0'
implementation 'com.squareup.okhttp:okhttp-urlconnection:2.2.0'
}
```
**Maven의 동적 의존성 해결을 피하라**
`2.1.+`과 같이 동적으로 버전을 정하는 방식은 불안정하고, 빌드 사이에 미묘하고 이해하기 어려운 차이를 초래할 수 있어 피하도록 하자. `2.1.1`처럼 정적으로 고정된 버전을 사용하는 것이 보다 안정적이고, 예측 가능하고, 반복적인 개발 환경을 구성하는 데에 도움이 될 것이다.
### IDE와 텍스트 에디터
**프로젝트 구조를 다루는 데에 용이한 에디터라면 무엇이든 사용해도 좋다.** 에디터는 개인적인 선택이고, 그 에디터가 프로젝트 구조와 프로젝트 빌드 시스템에 따라 기능하도록 하는 것은 개발자의 몫이다.
현재 가장 추천하는 IDE는 [Android Studio](https://developer.android.com/sdk/installing/studio.html)이다. Google이 개발했고, Gradle에 가장 밀접하며, 기본적으로 새로운 프로젝트 구조를 사용하는데다가 안정화 단계에 들어가 Android 개발에 잘 맞추어져 있기 때문이다.
원한다면 [Eclipse ADT](https://developer.android.com/sdk/installing/index.html?pkg=adt)를 사용해도 좋지만, 빌드하는 데에 낡은 프로젝트 구조와 Ant를 사용하기 때문에 이에 대한 설정이 필요하다. Vim, Sublime Text, Emacs같은 플레인 텍스트 에디터를 사용할 수도 있다. 이 경우에는 Gradle과 `adb`를 커맨드라인에서 사용해야 한다. Eclipse의 Gradle 사용이 제대로 작동하지 않는다면, 커맨드라인으로 빌드하거나 Android Studio로 옮기자. ADT 플러그인이 deprecate되었기 때문에, 이 것이 가장 좋은 옵션일 것이다.
무엇을 사용하든, 애플리케이션 빌드의 공식적인 방법인 Gradle과 새로운 프로젝트 구조를 따르고, 특정 에디터를 따르는 설정 파일을 버전 관리 시스템에 추가하는 것을 피하는 것만 명심하자. 예를 들면, Ant의 `build.xml` 파일들은 추가하지 않도록 한다. 특히 Ant의 빌드 설정을 변경하고 있다면 `build.gradle`을 최신의 상태로 기능하도록 하는 것을 잊지말자. 또한 다른 개발자들에게 친절해지자. 그들의 설정을 바꾸도록 강요하지 않아야한다.
### 라이브러리
**[Jackson](http://wiki.fasterxml.com/JacksonHome)**은 Object를 JSON으로, 혹은 그 반대로 변환해주는 Java 라이브러리이다. [Gson](https://code.google.com/p/google-gson/)이 이 문제를 해결하는 데에 많이 쓰이긴 하지만, 스트리밍, 인메모리 트리 모델, 전통적인 JSON-POJO 데이터 바인딩과 같은 여러 대안들을 지원하는 Jackson이 더 고성능일 것이다. 하지만 명심하자. Jackson이 GSON보다 더 큰 라이브러리이기 때문에, 65,000 메소드 수 제한에 부딪힌 경우라면 GSON을 사용하는 것이 나을 수도 있다. 다른 대안으로는 [Json-smart](https://code.google.com/p/json-smart/)과 [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/)를, 효율적인 HTTP 요청에는 [OkHttp](http://square.github.io/okhttp/)를 고려해보자. 이 모든 세가지의 라이브러리들은 같은 회사에서 개발되어 서로 상호보완이 매우 용이하다. [OkHttp can also be used in connection with Volley](http://stackoverflow.com/questions/24375043/how-to-implement-android-volley-with-okhttp-2-0/24951835#24951835).
**RxJava** 비동기 이벤트를 처리하는 Reactive Programming을 위한 라이브러리이다. 이는 매우 강력하고 유망한 패러다임으로, 너무 다른 점이 많아 혼란스러울 수 있다. 모든 애플리케이션에서 아키텍트들에게 이 라이브러리를 쓰기 전 주의할 것을 추천한다. 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 혹은 다른 pre-JDK8 플랫폼에서 Lambda 표현 문법을 사용할 수 있도록 하는 Java 라이브러리이다. 이 라이브러리는 특히 RxJava와 같이 기능 위주 스타일의 코드를 더욱 타이트하고 읽기 좋게 만들어준다. 사용하려면, JDK8을 설치하고 이를 Android Studio 프로젝트 대화상자에서 SDK 경로로 설정한 후, `JAVA8_HOME`과 `JAVA7_HOME` 환경변수를 설정한 뒤 프로젝트 root의 build.gradle을 이렇게 설정한다:
```groovy
dependencies {
classpath 'me.tatarka:gradle-retrolambda:2.4.1'
}
```
그리고 각각 모듈들의 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 lambda의 코드 지원을 제공한다. 만약 lambda가 처음이라면, 다음 항목들을 따라 시작해보자:
- 하나의 메소드를 갖는 모든 인터페이스들은 "lambda와 밀접"하고, 더욱 타이트한 문법으로 묶일 수 있다.
- 만약 파라메터들이 의심스럽다면, 일반 익명 내부 클래스를 작성하고 Android Studio가 lambda로 묶어주도록 해보자.
**Dex 메소드 제한을 유의하고, 많은 라이브러리 사용을 피하자.** Android 앱들이 dex 파일로 패키징될 때, 65,536개의 참조 메소드 수 제한을 갖는다[[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/). 제한된 메소드 수를 넘어서면 컴파일시 Fatal error를 보게될 것이다. 그렇기 때문에, 최소한의 라이브러리들을 사용하고, [dex-method-counts](https://github.com/mihaip/dex-method-counts) 툴을 사용하여 제한된 수보다 적게 유지하기 위해 어떤 라이브러리들을 사용할지 결정하자. 특히 Guava 라이브러리는 피하자. 이 라이브러리는 13,000개가 넘는 메소드를 가지고 있다.
### Activity와 Fragment
Fragment와 Activity를 이용하여 Android의 구조를 가장 좋은 방법으로 설계하는 방법은 커뮤니티나 Futurice 개발자들 사이에서도 합의된 바가 없다. Square는 Fragment의 우회 대안으로 [a library for building architectures mostly with Views](https://github.com/square/mortar)를 제공하는데, 이 또한 커뮤니티 사이에서 널리 추천할만한 방식은 아니라고 생각된다.
Android API의 히스토리로 인해, 막연히 Fragment가 화면상의 UI 조각이라고 떠올릴 수 있을 것이다. 즉, Fragment는 일반적으로 UI와 연관되어 있다는 것이다. Activity 또한 막연하게 그들의 라이프사이클과 상태를 관리하는 데에 중요한 컨트롤러라고 생각할 수 있다. 그러나, 다음 역할들에서 차이를 쉽게 찾아볼 수 있다: Activity는 UI 역할을 수행하고([delivering transitions between screens](https://developer.android.com/about/versions/lollipop.html)), [Fragment는 독립적으로 컨트롤러의 역할을 수행한다](http://developer.android.com/guide/components/fragments.html#AddingWithoutUI). 그래서 우리는 Fragment 혹은 Activity, 또는 View 셋 중 하나만을 이용한 구조를 선택함에 있어서 결함이 있다는 점을 파악하고 정확한 근거를 갖는 결정을 하여 조심스럽게 시작하기를 권한다. 다음은 주의해야 할 것들에 대한 조언인데, 적당히 걸러서 수용하자:
- [Nested fragments](https://developer.android.com/about/versions/android-4.2.html#NestedFragments)를 널리 사용하는 것은 피해야 하는데, [matryoshka bugs](http://delyan.me/android-s-matryoshka-problem/)가 발생할 수 있기 때문이다. 중첩된 Fragment는 꼭 타당한 경우(예를 들면, 수평으로 슬라이딩하는 ViewPager 내부의 Fragment들)나 잘 설명할 수 있을 만한 경우에만 사용하자.
- Activity에 너무 많은 코드를 넣는 것을 피해야 한다. 가능하면 언제든지 가벼운 컨테이너로서 유지하고, 주로 라이프사이클과 다른 중요한 Android와의 인터페이싱 API를 위해서만 존재하도록 하자. 순수 Activity 보다는 단일 Fragment로 구성된 Activity가 좋다 - UI 코드를 Activity의 Fragment에 넣자. 이는 다른 잘 구성된 레이아웃, 혹은 여러 Fragment로 구성된 타블렛 화면으로 옮길 필요가 있을 때에 재사용이 가능하도록 만든다. 정확한 근거가 없는 결정이라면 Fragment와 상호 작용하지 않는 Activity는 피하자.
- 앱의 내부적 동작이 Intent에 강하게 의존적인 Android 레벨의 API를 남용해서는 안된다. 이는 버그와 렉을 유발하여 Android OS나 다른 애플리케이션들에 영향을 줄 수 있다. 예를 들어, 만약 앱이 당신의 패키지 사이에서 내부적인 커뮤니케이션을 위해 Intent를 사용한다면, 앱이 OS 부팅 바로 후에 실행되었을 때 사용자 경험 상에서 몇 초간의 렉을 발생시킬 수 있다고 알려져 있다.
### Java 패키지 설계
Android 애플리케이션을 위한 Java 설계 간단히 [Model-View-Controller](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) 간략화할 수 있다. Android에서는, [Fragment and Activity are actually controller classes](http://www.informit.com/articles/article.aspx?p=2126865) 라고 설명되는데, 다른 면에서 Fragment와 Activity들은 명시적으로 사용자 인터페이스, 즉 View 이기도 하다.
그렇기 때문에, Fragment(혹은 Activity)를 정확히 Conteroller나 View 구분할 수 없다. 그래서 그에 해당하는 `fragments` 패키지에 두는 것이 낫다. Activity는 이전 섹션에서의 조언에 따라 최상위 패키지에 둘 수 있다. 2, 3개 이상의 Activity들을 계획하고 있다면, `activities` 패키지를 만들어 두자.
다른 경우에는, API 응답에 대한 JSON 파서에 의해 채워진 POJO들을 담는 `models` 패키지, 커스텀 View, Notification, Action bar view, Widget 등을 담는 `views` 패키지를 두어 설계가 일반적인 MVC로 표현될 수 있다. Adapter들은 데이터와 View들 사이에 존재하는 gray matter라고 할 수 있지만, 일반적으로 `getView()`를 통해 View들을 추출하는 데에 필요하기 때문에 `views` 패키지 안에 `adapters`라는 서브패키지로 둘 수 있다.
Controller 클래스들은 애플리케이션 전체에, Android 시스템에 가깝게 존재한다. 이들은 `managers` 패키지에 둘 수 있다. "DateUtils"와 같은 다양한 데이터 처리 클래스들은 `utils` 패키지에, 백엔드와 인터랙션하는 역할을 맡는 클래스들은 `network` 패키지에 두자.
종합적으로, 백엔드와 가까운 것부터 유저와 가까운 순서대로 정렬해보면 이렇다:
```
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` 속성은 맨 아래에
- 태그를 닫는 `/>`는 정렬과 새 속성 추가를 위해 독립적인 줄에
- Rather than hard coding `android:text`에 하드코딩하는 것보다, Android Studio에서 사용 가능한 [Designtime attributes](http://tools.android.com/tips/layout-designtime-attributes)를 고려하자.
```xml
<?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_****`를 레이아웃 XML에 두고 `android:****`를 스타일 XML에 정의하는 것이다. 이 규칙은 예외가 있지만, 일반적으로 잘 동작한다. 이는 레이아웃(위치, 여백, 크기)과 내용에 관한 속성을 레이아웃 파일에 두고, 상세한 모양(색, 안쪽 여백, 폰트)에 대한 내용은 스타일 파일에 두기 위함이다.
예외는 이런 경우가 있다:
- `android:id`는 정확히 레이아웃 파일에 두어야 한다. should obviously be in the layout files
- `LinearLayout`의 `android:orientation` 속성은 일반적으로 레이아웃 파일에 있는 것이 타당하다.
- `android:text`는 내용을 정의하는 속성이기 때문에 레이아웃 파일에 두어야 한다.
- 가끔 일반적인 스타일로 `android:layout_width`와 `android:layout_height` 속성들을 두어야 말이 될 것 같지만, 기본적으로 이들은 레이아웃 파일에 보여진다.
**스타일을 사용하자.** View에 중복되는 모양이 사용되는 것은 매우 일반적인 일이기 때문에, 거의 모든 프로젝트들이 스타일을 적절히 사용해야한다. 적어도 애플리케이션에서 대부분의 텍스트 내용들은 일반 스타일을 가져야한다. 예를 들면 이렇다:
```xml
<style name="ContentText">
<item name="android:textSize">@dimen/font_normal</item>
<item name="android:textColor">@color/basic_black</item>
</style>
```
TextView에 적용해보면:
```xml
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/price"
style="@style/ContentText"
/>
```
아마 버튼들에도 같은 일을 해주어야 하겠지만, 멈추지 말자. 계속 진행하면서 연관되어있고 중복된 `android:****` 속성들을 일반 스타일로 묶자.
**큰 스타일 파일은 다른 파일들로 나누자.** 단 하나의 `styles.xml` 파일을 가질 필요는 없다. Android SDK는 박스 외부의 파일들도 지원하는데, `styles`라는 이름엔 전혀 마법같은 무언가가 없이 파일 안에 `<style>` XML 태그가 있다면 상관없다. 그렇기 때문에 파일 이름은 `styles.xml`, `styles_home.xml`, `styles_item_details.xml`, `styles_forms.xml` 등과 같이 정할 수 있다. 빌드 시스템에서 의미를 갖는 리소스 디렉토리와는 다르게 `res/values` 안의 파일명은 임의로 설정 가능하다.
**`colors.xml`는 색 팔렛트이다.** `colors.xml`에는 색 이름과 RGBA 값을 매핑해놓는 일 외에 더 해야할 일은 없다. 각각 다른 버튼 타입들에 RGBA 값들을 정의하지 않도록 하자.
*아래의 방식은 피하자:*
```xml
<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`이 아닌 스타일에 더 적합하다.
대신, 이렇게 하자:
```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" 같은 이름들이 더욱 받아들이기 쉽다. 이렇게 색의 형식을 지정하게 되면 색 값들을 리팩토링하기 쉬워지고, 얼마나 많은 색들이 사용되고 있는지 명시적으로 알 수 있다. 보통 심미감을 중요시하는 앱에서는, 사용되는 색의 종류를 줄이는 것이 중요하다.
**dimens.xml을 colors.xml처럼 다루자.** 기본적으로 색과 같은 목적을 위해 일반적인 여백과 폰트 크기 등의 "팔렛트"를 정의하자. 다음은 dimens.xml 파일의 좋은 예시이다:
```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>
```
문자열들이 일반적으로 다루어지듯이, 레이아웃, 바깥쪽/안쪽 여백에 하드코딩된 값들 대신 `spacing_****`을 사용하자. 이는 스타일과 레이아웃을 체계화하고 변경하는 것을 쉽게 해줌과 동시에 일관된 룩앤필(Look-and-feel)을 제공한다.
**strings.xml**
strings.xml의 문자열들은 네임스페이스의 형태와 비슷하게 이름을 짓고, 2개 이상의 Key에 값을 중복해서 사용하는 것을 두려워하지 않도록 하자. 언어는 복잡하기 때문에, 네임스페이스가 문맥을 갖고 애매함을 없애는 것이 필수이다.
**잘못된 예**
```xml
<string name="network_error">Network error</string>
<string name="call_failed">Call failed</string>
<string name="map_failed">Map loading failed</string>
```
**좋은 예**
```xml
<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>
```
문자열 값을 모두 대문자로 쓰지 않도록 한다. 일반적인 텍스트 컨벤션을 따르되(e.g., 첫 글자만 대문자로), 만약 문자열을 모두 대문자로 표시해야 한다면, TextView의 [`textAllCaps`](http://developer.android.com/reference/android/widget/TextView.html#attr_android:textAllCaps) 속성을 이용하자.
**잘못된 예**
```xml
<string name="error.message.call">CALL FAILED</string>
```
**좋은 예**
```xml
<string name="error.message.call">Call failed</string>
```
**깊은 View 계층을 피하자.** 가끔 View를 편성하기 위해 LinearLayout을 하나 더 추가하려할 것이다. 이 상황은 이러한 문제를 일으킨다:
```xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<RelativeLayout
...
>
<LinearLayout
...
>
<LinearLayout
...
>
<LinearLayout
...
>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
```
레이아웃 파일에서 명시적으로 이런 형태를 보지 못했다 하더라도, 결국 다른 View에 또 다른 View들을 채울 때(Java에서) 이렇게 끝나게 될 것이다.
두 가지 문제가 발생한다. 프로세서가 다루어야 할 UI 트리가 복잡해지면서 퍼포먼스 문제를 경험하게 될 것이다. 또 하나의 심각한 문제는 [StackOverflowError](http://stackoverflow.com/questions/2762924/java-lang-stackoverflow-error-suspected-too-many-views)의 가능성이다.
그러므로, 최대한 View 계층을 수평하게 유지하자: [RelativeLayout](https://developer.android.com/guide/topics/ui/layout/relative.html)를 사용하는 방법, [optimize your layouts](http://developer.android.com/training/improving-layouts/optimizing-layout.html) 방법과 [`<merge>` tag](http://stackoverflow.com/questions/8834898/what-is-the-purpose-of-androids-merge-tag-in-xml-layouts)를 사용하는 방법을 확인하자.
**WebView와 관련된 문제들에 유의하자.** 뉴스 기사와 같은 웹페이지를 보여주어야 할 때, 백엔드 프로그래머들에게 "*순수한*" HTML을 요청하지 않고 HTML을 정리하기 위해 클라이언트단에서 프로세싱하는 것을 피하자. ApplicationContext가 아닌 Activity로의 참조를 유지할 때, [WebViews can also leak memory](http://stackoverflow.com/questions/3130654/memory-leak-in-webview). 간단한 텍스트와 버튼을 사용할 때에는 WebView를 피하고 TextView와 Button을 사용하도록 한다.
### 테스트 프레임워크
Android SDK의 테스팅 프레임워크는 여전히 미흡한데, 특히 UI 테스트에 관해서는 더더욱 그렇다. Android Gradle은 현재 [extension of JUnit with helpers for Android](http://developer.android.com/reference/android/test/package-summary.html)을 사용하여 만들어진 JUnit 테스트들을 실행하는 [`connectedAndroidTest`](http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Testing)라는 테스트 Task를 구현하고 있다. 이는 기기 혹은 에뮬레이터에 연결된 테스트를 실행해야 할 것을 의미한다. 공식 가이드인 [[1]](http://developer.android.com/tools/testing/testing_android.html) [[2]](http://developer.android.com/tools/testing/activity_test.html)를 따라 테스트하자.
**View가 아닌 유닛 테스트에는 [Robolectric](http://robolectric.org/)를 사용하자.** 이는 개발 속도의 만족을 위해 "기기에 연결되지 않은" 테스트의 제공을 추구하는 테스트 프레임워크이다. 특히 모델과 View 모델들의 유닛 테스트에 적합하다. 하지만, UI 테스트에서의 Robolectric를 사용한 테스트는 부정확하고 불완전하다. 애니메이션, 대화 상자 등에 관한 UI 요소들의 테스트에서는 문제를 보일 것이고, 이는 "어둠 속에서 걷는 것"(조작할 만한 화면을 보지 않고 테스트)처럼 매우 복잡할 것이다.
**[Robotium](https://code.google.com/p/robotium/)은 UI 테스트 작성을 쉽게 해준다.** UI의 연결된 테스트를 실행하는 데에 Robotium이 필요하지 않지만, View를 가져와 분석하고, 화면을 조작할 수 있게 해주는 Robotium의 많은 도우미들이 유용하게 작용할 것이다. 테스트 케이스는 이처럼 쉽게 표현된다:
```java
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"));
```
### 에뮬레이터
전문적으로 Android 앱을 개발하고 있다면, [Genymotion emulator](http://www.genymotion.com/)의 라이센스를 구매하자. Genymotion 에뮬레이터는 일반적인 AVD 에뮬레이터에 비해 빠른 FPS로 실행된다. 앱을 데모하고, 네트워크 연결 품질, GPS 포지션을 시험해보는 툴들을 제공한다. 또한 연결된 테스트에 이상적으로 작동한다. 많은(전부는 아님) 다른 기기들을 접할 때, Genymotion의 라이센스는 실제 여러가지 기기를 구매하는 것 보다 훨씬 저렴하다.
주의할 점: Genymotion 에뮬레이터는 Google Play Store와 Maps과 같은 모든 Google 서비스들을 포함하고 있지는 않다. 삼성의 특정 API를 테스트해야 한다면, 실제 삼성 기기가 필요하다.
### Proguard 설정
[ProGuard](http://proguard.sourceforge.net/)는 일반적으로 Android 프로젝트의 패키징된 코드를 축소하고, 난독화하기 위해 사용된다.
ProGuard를 사용하고 있는지 아닌지는 해당 프로젝트 설정에 달려있다. 보통 릴리즈 apk를 빌드할 때 gradle을 설정하고 ProGuard를 사용할 것이다.
```groovy
buildTypes {
debug {
minifyEnabled false
}
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
```
어떤 코드가 보존되어야 하고 어떤 코드가 버려지거나 난독화되어야할 지 결정하기 위해, 코드에 한 개 이상의 엔트리 포인트를 설정해야한다. 이 엔트리 포인트는 일반적으로 main 메소드, applets, midlets, Activity와 같은 것들이다.
Android 프레임워크는 `SDK_HOME/tools/proguard/proguard-android.txt`에서 찾아볼 수 있는 기본 설정을 사용한다. 위의 설정을 사용하여, `my-project/app/proguard-rules.pro`의 사용자화된 특정 프로젝트 ProGuard 규칙은 기본 설정에 추가로 설정될 것이다.
ProGuard와 관련된 일반적인 문제는 어떠한 Warning도 없이 빌드 커맨드가 성공했는데도 애플리케이션이 시작시에 `ClassNotFoundException`나 `NoSuchFieldException`와 비슷한 예외로 크래시가 발생하는 것이다.
이는 두 가지 중 하나를 의미한다:
1. ProGuard가 클래스, Enum, 메소드, 필드 혹은 어노테이션들을 필요하지 않다고 여겨 제거한 것이다.
2. ProGuard가 클래스, Enum, 필드 이름들이 간접적으로 고유의 이름대로 사용되고 있음에도 불구하고(예를 들면 Java reflection을 통해) 이들을 난독화(이름 변경)한 것이다.
물음의 객체가 제거되었는지 보려면 `app/build/outputs/proguard/release/usage.txt`을 확인하자.
물음의 객체가 난독화되었는지 보려면 `app/build/outputs/proguard/release/mapping.txt`을 확인하자.
ProGuard가 필요한 클래스 혹은 클래스 멤버들을 *벗겨내는 것*을 막기 위해서는, ProGuard 설정에 `keep` 옵션을 추가하자:
```
-keep class com.futurice.project.MyClass { *; }
```
ProGuard가 클래스 혹은 클래스 멤버들의 *난독화*을 막고싶다면, `keepnames`을 추가하자:
```
-keepnames class com.futurice.project.MyClass { *; }
```
또 다른 예시로 [Proguard](http://proguard.sourceforge.net/#manual/examples.html)를 읽어보자.
**프로젝트의 초기에, 릴리즈 빌드를 만들자.** 이는 ProGuard 규칙들이 중요한 것들을 정확하게 보관하고 있는지 확인하기 위함이다. 또한 언제든지 새로운 라이브러리를 포함시켰을 때, 릴리즈 빌드를 만들고 기기에서 apk를 테스트해보자. 릴리즈 빌드를 만들기 위해 앱이 "1.0" 버전이 되기까지 기다리지 말고, 수 차례 의외의 문제들을 발견하고 수정하는 짧은 시간을 갖자.
**팁.** 배포시에 `mapping.txt` 파일들은 매 릴리즈마다 저장하자. 각 릴리즈 빌드마다 `mapping.txt` 파일을 보관해두면, 사용자가 버그를 만나고 알아보기 힘든 스택 트레이스를 보내왔을 때 문제를 확실하게 디버깅할 수 있다.
**DexGuard**. 릴리즈 코드를 최적화하고, 특히 알기 어렵게 만들기 위해 하드코어한 툴이 필요하다면, ProGuard를 빌드한 같은 팀에서 만든 상업 소프트웨어인 [DexGuard](http://www.saikoa.com/dexguard)를 고려해보자. 이는 65,000 메소드 수 제한을 해결하기 위해 Dex 파일들을 쉽게 나눈다.
### 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 Oy](http://www.futurice.com)
Creative Commons Attribution 4.0 International (CC BY 4.0)
Translation
===========
Translated to Korean (`ko`) by **[Minsoo Park](http://www.github.com/minsoopark)**.
Original content by [Futurice Oy](http://www.futurice.com).
================================================
FILE: translations/Portuguese/README.pt.md
================================================
# As melhores práticas no desenvolvimento Android
Lições aprendidas por programadores Android na [Futurice](http://www.futurice.com). Evite reinventar a roda seguindo estas recomendações. Se está interessado em desenvolver aplicações para iOS ou Windows Phone, não se esqueça de verificar também nossas [**iOS Good Practices**](https://github.com/futurice/ios-good-practices) e [**Windows App Development Best Practices**](https://github.com/futurice/windows-app-development-best-practices).
[](https://android-arsenal.com/details/1/1091)
## Resumo
#### Use o Gradle e a estrutura do projeto por ele recomendada
#### Coloque suas senhas e dados sensíveis em gradle.properties
#### Não escreva o seu próprio cliente de HTTP, utilize as bibliotecas Volley ou OkHttp
#### Use a biblioteca Jackson para fazer o parse de JSON
#### Evite o Guava e utilize poucas bibliotecas devido ao *limite máximo de 65 mil métodos*
#### Use Fragmentos para representar um ecrã UIƒ
#### Use Atividades apenas para gerir Fragmentos
#### As layouts de XML são código, organize-as bem
#### Use estilos para evitar atributos duplicados nas layouts de XML
#### Use múltiplos ficheiros de estilos para evitar apenas um muito grande
#### Mantenha o seu colors.xml pequeno e DRY (sem repetições), defina apenas a palete
#### Mantenha também o dimens.xml DRY, defina constantes genéricas
#### Não faça uma hierarquia muito profunda de ViewGroups
#### Evite processar WebViews no cliente, e cuidado com fugas
#### Use o Robolectric para testes unitários, Robotium para testes conetados (UI)
#### Use sempre o ProGuard ou DexGuard
#### Use SharedPreferences para persistência simples, caso contrário use ContentProviders
----------
### Android SDK
Coloque o [Android SDK](https://developer.android.com/sdk/installing/index.html?pkg=tools) algures no seu diretório inicial ou algum outro local independente da aplicação . Alguns IDEs incluem o SDK aquando da instalação, e pode colocá-lo sob o mesmo diretório que o IDE. Isso pode ser ruim quando precisa de atualizar (ou reinstalar) o IDE, ou quando mudar IDEs . Além disso, evite colocar o SDK noutro diretório em nível de sistema que pode precisar de permissões sudo, se o IDE é executado sob o utilizador e não na raiz.
### Sistema de compilação
A sua opção padrão deve ser o [Gradle](http://tools.android.com/tech-docs/new-build-system). O Ant é muito mais limitado e também mais verboso. Com o Gradle, é fácil:
- Construir sabores ou variantes diferentes do seu aplicativo
- Fazer tarefas simples tipo script
- Gerir e fazer o download de dependências
- Personalizar keystores
- E mais
O plugin Gradle do Android também está a ser activamente desenvolvido pela Google como o novo sistema de compilação padrão.
### Estrutura do Projeto
Existem duas opções populares: a estrutura do projeto antiga Ant juntamente com o Eclipse ADT, e a nova estrutura do projeto Gradle juntamente com o Android Studio. Você deve escolher a nova estrutura do projeto. Se o seu projeto usa a estrutura antiga, considere-a desatualizada e comece a transferi-la para a nova estrutura.
Estrutura antiga:
```
old-structure
├─ assets
├─ libs
├─ res
├─ src
│ └─ com/futurice/project
├─ AndroidManifest.xml
├─ build.gradle
├─ project.properties
└─ proguard-rules.pro
```
Estrutura nova:
```
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
```
A principal diferença é que a nova estrutura separa explicitamente 'source sets' (`main`, `androidTest`), um conceito do Gradle. Você poderá, por exemplo, adicionar diretórios 'paid' e 'free' a `src` que terá o código fonte para os sabores pagos e gratuitos da sua aplicação.
Ter um diretório de topo `app` é útil para distinguir a sua aplicação de outras bibliotecas (por exemplo, `library-foobar`) que terá uma referência na sua aplicação. O `settings.gradle` posteriormente mantém referências para estas bibliotecas, e o `app/build.gradle` pode fazer referência a elas.
### Configuração do Gradle
**Estrutura geral.** Siga [o guia do Google sobre Gradle para Android](http://tools.android.com/tech-docs/new-build-system/user-guide)
**Pequenas tarefas.** Ao invés de scripts (shell, Python, Perl, etc.), pode fazer tarefas com o Gradle. Siga [a documentação do Gradle](http://www.gradle.org/docs/current/userguide/userguide_single.html#N10CBF) para mais detalhes.
**Senhas.** No `build.gradle` da sua aplicação, precisa de definir `signingConfigs` para a _compilação de lançamento_. O que deverá evitar:
_Não faça isto_. Irá aparecerá no sistema de controlo de versão.
```groovy
signingConfigs {
release {
storeFile file("myapp.keystore")
storePassword "password123"
keyAlias "thekey"
keyPassword "password789"
}
}
```
Ao invés, crie o ficheiro `gradle.properties` o qual _não_ deverá ser adicionado ao sistema de controle de versão:
```
KEYSTORE_PASSWORD=password123
KEY_PASSWORD=password789
```
Aquele ficheiro será importado automaticamente pelo gradle, podendo utilizá-lo no `build.gradle` da seguinte forma:
```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.")
}
}
}
```
**Prefira a resolução de dependências Maven em vez de importar ficheiros jar.** Se você incluir explicitamente os ficheiros jar no seu projeto, eles terão uma versão fixa específica, como `2.1.1`. Baixar jars e gerir atualizações é uma confusão, este é o problema que o Maven resolve de forma adequada, e é também incentivada nas compilações de Android Gradle. Por exemplo:
```groovy
dependencies {
implementation 'com.squareup.okhttp:okhttp:2.2.0'
implementation 'com.squareup.okhttp:okhttp-urlconnection:2.2.0'
}
```
**Evite a resolução de dependências dinâmicas do Mavem**
Evite usar versões dinâmicas, como por exemplo `2.1.+`, isto porque podem resultar em compilações instáveis ou subtis diferenças de comportamento não rastreadas entre as compilações. O uso de versões estáticas como `2.1.1` ajuda a criar um ambiente de desenvolvimento mais estável, previsível e repetível.
### IDEs e editores de texto
**Use qualquer editor, mas este deverá integrar bem com a estrutura do projeto.** Os editores são uma escolha pessoal, contudo é sua responsabilidade colocar o editor a funcionar de acordo com a estrutura do projeto e o sistema de compilação.
O IDE mais recomendado neste momento é o [Android Studio](https://developer.android.com/sdk/installing/studio.html), porque foi e está a ser desenvolvido pela Google, é o mais próximo do Gradle, usa a nova estrutura de projeto por definição, encontra-se em fase estável, e foi adaptado especificamente para desenvolvimento Android.
Pode usar o [Eclipse ADT](https://developer.android.com/sdk/installing/index.html?pkg=adt) se assim o desejar, mas terá de o configurar, visto que ele espera a antiga estrutura do projeto e compilações Ant. Poderá até mesmo usar editores de texto simples como o Vim, Sublime Text, ou Emacs. Nesse caso, irá ter de usar Gradle e `adb` na linha de comando. Se a integração do Eclipse com o Gradle não está a funcionar corretamente, as suas opções são usar a linha de comando apenas para compilação, ou migrar para o Android Studio. A última é a melhor opção visto que o plugin ADT foi depreciado recentemente.
O que quer que use, certifique-se apenas que o Gradle e a nova estrutura do projeto se mantêm como a forma oficial de compilar a sua aplicação, e evite adicionar os ficheiros com as configurações específicas do seu editor ao sistema de controlo de versão. Por exemplo, evite adicionar o ficheiro Ant `build.xml`. Especialmente não se esqueça de manter o `build.gradle` atualizado e em funcionamento se está a modificar as configurações de compilação do Ant. Além disso, seja gentil com outros programadores, não os force a alterar as suas ferramentas preferidas.
### Bibliotecas
**[Jackson](http://wiki.fasterxml.com/JacksonHome)** é uma biblioteca em Java para converter Objetos em JSON e vice-versa. [Gson](https://code.google.com/p/google-gson/) é uma escolha popular para resolver este problema, contudo nós achamos que o Jackson é mais eficiente visto que suporta maneiras alternativas de processar JSON: _streaming_, modelo de árvore em memória, e a vinculação tradicional JSON-POJO. Mantenha em mente, contudo, que o Jackson é uma biblioteca maior do que Gson, assim, dependendo do seu caso, pode preferir Gson para evitar a limitação dos 65 mil métodos. Outras alternativas: [Json-smart](https://code.google.com/p/json-smart/) e [Boon JSON](https://github.com/RichardHightower/boon/wiki/Boon-JSON-in-five-minutes)
**Trabalhos em rede, armazenamento em cache e imagens.** Há um par de soluções testadas exaustivamente no dia-a-dia para a realização de requisições para servidores, que você deverá utilizar antes de considerar implementar o seu próprio cliente. Use o [Volley](https://android.googlesource.com/platform/frameworks/volley) ou o [Retrofit](http://square.github.io/retrofit/). O Volley também fornece ajudantes para carregar e fazer o _cache_ de imagens. Se escolher o Retrofit, considere o [Picasso](http://square.github.io/picasso/) para carregar e fazer o _caching_ de imagens, e [OkHttp](http://square.github.io/okhttp/) para requisições eficientes de HTTP. Todos os três, Retrofit, Picasso e OkHttp foram criados pela mesma empresa, portanto eles complementam-se muito bem. [OkHttp pode também ser utilizado em conexão com o Volley](http://stackoverflow.com/questions/24375043/how-to-implement-android-volley-with-okhttp-2-0/24951835#24951835).
**RxJava** é uma biblioteca para Reactive Programming, por outras palavras, manipulação de eventos assíncronos. É um paradigma poderoso e promissor, o que pode tornar-se confuso devido ao fato de ser tão diferente. Recomendamos que tenha cuidado antes de usar esta biblioteca para arquitetar toda a sua aplicação. Há alguns projetos feitos por nós usando RxJava, se precisar de ajuda fale com uma destas pessoas: Timo Tuominen, Olli Salonen, Andre Medeiros, Mark Voit, Antti Lammi, Vera Izrailit, Juha Ristolainen. Escrevemos também algumas postagens no blogue sobre ele: [[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).
Se você não tiver qualquer experiência com Rx, comece por aplicá-lo apenas nas respostas da API. Alternativamente, comece aplicando-o para a simples manipulação de eventos UI, como eventos de clique ou eventos de digitação num campo de pesquisa. Se você está confiante nas capacidades de Rx e quer aplicá-lo em toda a arquitetura, então, escreva os Javadocs em todas as partes mais difíceis. Mantenha em mente que outro programador que não esteja familiarizado com o RxJava pode ter dificuldades em manter o projeto. Faça o seu melhor para ajudá-los a compreender o seu código e também Rx.
**[Retrolambda](https://github.com/evant/gradle-retrolambda)** é uma biblioteca em Java que permite o uso de expressões lambda em Android ou outras plataformas antes da JDK8. Ajuda a manter o código limpo e legível, especialmente se usa um estilo de programação funcional como pode exemplo o RxJava. Para o usar, instale a JDK8, defina-a como a sua localização SDK na janela da estrutura do projeto no Android Studio, defina as variáveis de ambiente `JAVA8_HOME` e `JAVA7_HOME`, e no ficheiro build.gradle que se encontra na raiz do projeto:
```groovy
dependencies {
classpath 'me.tatarka:gradle-retrolambda:2.4.1'
}
```
e no ficheiro build.gradle de cada módulo, coloque
```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
}
```
Após fazer isso, o Android Studio irá proporcionar suporte ao código que escreve para lambdas Java8. Se é novo no mundo dos lambdas, use o seguinte para começar:
- Qualquer interface com apenas um método é "amiga dos lambdas" e pode ser compressa na sintaxe mais compacta proporcionada pelos lambdas;
- Se está na dúvida sobre parâmetros e isso, escreva uma classe interna anónima normal e deixe o Android Studio a comprimir num lambda automaticamente.
**Esteja atento à limitação do número de métodos dex, e evite usar demasiadas bibliotecas.** As aplicações de Android, quando organizadas como um arquivo dex, têm um limite de 65536 métodos aos quais se podem fazer referências [[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/). Durante a compilação irá encontrar um erro fatal se passar esse limite. Por essa razão, use o número mínimo de bibliotecas, e use a ferramenta [dex-method-counts](https://github.com/mihaip/dex-method-counts) para encontrar que bibliotecas usar para ficar abaixo desse limite. Especialmente evite usar a biblioteca Guava, visto que contém mais de 13 mil métodos.
### Atividades e Fragmentos
Não há um consenso dentro da comunidade nem entre os programadores da Futurice como melhor organizar a arquitetura do Android com Fragmentos e Atividades. O Square até tem
[uma biblioteca para construir uma arquitetura maioritariamente com Views](https://github.com/square/mortar), descartando a necessidade de Fragmentos, contudo isso ainda não é considerado uma boa prática dentro da comunidade.
Devido à história das APIs Android, pode de forma ligeira considerar Fragmentos como peças de UI no ecrã. Por outras palavras, Fragmentos estão normalmente relacionados com UI. Atividades podem ser ligeiramente consideradas controladores, eles são especialmente importantes devido ao seu ciclo de vida e para gerir estados. Contudo, não se surpreenda se vir variações nestes papéis: atividades podem tomar papéis na UI ([fazendo transições entre ecrãs](https://developer.android.com/about/versions/lollipop.html)), e [fragmentos podem ser usados apenas como controladores](http://developer.android.com/guide/components/fragments.html#AddingWithoutUI). Sugerimos que navegue com cuidado, tomando decisões informadas visto que há contrapartidas ao uso de arquiteturas apenas com fragmentos, ou apenas com atividades, ou apenas com views. Seguem alguns conselhos do que ter mais cuidado, mas leve-os com uma pitada de sal:
- Evite usar [fragmentos dentro de fragmentos](https://developer.android.com/about/versions/android-4.2.html#NestedFragments) em excesso, porque [matryoshka bugs](http://delyan.me/android-s-matryoshka-problem/) podem ocorrer. Use fragmentos dentro de fragmentos apenas quando isso faz sentido (por exemplo, fragmentos num ViewPager que desliza horizontalmente dentro de um fragmento tipo ecrã) ou se é uma decisão bem informada.
- Evite colocar demasiado código dentro das atividades. Sempre que possível, mantenha-as como contentores de peso leve, existindo primariamente na sua aplicação pelo ciclo de vida e outras interfaces importantes da API do Android. Dê preferência a atividades com apenas um fragmento ao invés de apenas atividades - coloque o seu código UI no fragmento da atividade. Isto irá fazer com que o código seja reutilizável no caso de por exemplo o querer modificar e colocar dentro de uma tabbed layout, ou num ecrã de tablete com múltiplos fragmentos. Evite ter uma atividade sem um fragmento correspondente, a não se que saiba o que está a fazer.
- Não abuse das APIs do Android, como pode exemplo colocar demasiado peso nos Intent para o funcionamento interno da sua aplicação. Isso pode afetar o sistema operativo do Android e outras aplicações, criando bugs ou lag. Por exemplo, é um fato que se utilizar Intents para a comunicação interna na sua aplicação entre os packages, poderá fazer com que exista um lag de alguns segundos na experiência do utilizador se a app foi aberta depois do sistema operativo tiver sido iniciado.
### Arquitetura dos pacotes de Java
A arquitetura do Java para aplicações Android pode ser mais ou menos aproximada ao modelo [Model-View-Controller](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller). No Android, [Fragmentos e Atividades são na verdade classes de controlo](http://www.informit.com/articles/article.aspx?p=2126865). Por outro lado, elas são parte explícita da interface do utilizador, portanto são também views.
Por este motivo, é difícil classificar fragmentos (ou atividades) como restritamente controladores ou views. É melhor as deixa ficar no seu próprio pacote `fragments`. Atividades podem ficar no pacote mais algo desde que sigam os conselhos da secção anterior. Se está a planear ter mais do que 2 ou 3 atividades, então crie também um pacote `activities`.
Caso contrário, a arquitetura pode ser semelhante ao típico MVS, com um pacote `models` contendo POJOs que serão populados através de JSON parser com respostas à API, e um pacote `views` contendo as Views criadas por si, notificações, views da action bar, widgets, etc. Adapters são uma matéria cinzenta, estando entre os dados e as views. Contudo, eles tipicamente precisam de exportar alguma View através do `getView()`, portanto poderá incluir o sub-pacote `adapters` dentro de `views`.
Algumas classes de controladores são para toda a aplicação e perto do sistema Android. Estas podem estar no pacote `managers`. Classes de processamento de dados várias, como "DateUtils", ficam dentro do pacote `utils`. Classes têm a responsabilidade de interagir com o servidor ficam no pacote `network`.
No final de contas, organizadas do mais perto do servidor para o mais perto do utilizador:
```
com.futurice.project
├─ network
├─ models
├─ managers
├─ utils
├─ fragments
└─ views
├─ adapters
├─ actionbar
├─ widgets
└─ notifications
```
### Recursos
**Dar o nome.** Siga a convenção de colocar o prefixo como tipo, por exemplo `type_foo_bar.xml`. Exemplos: `fragment_contact_details.xml`, `view_primary_button.xml`, `activity_main.xml`.
**Organizar os layout XMLs.** Se não tem a certeza de como formatar o layout XML, as seguintes convenções podem ajudar:
- Um atributo por linha, indentação de 4 espaços
- `android:id` sempre como o primeiro atributo
- `android:layout_****` atributo no topo
- `style` atributo no fundo
- A Tag de fechar `/>` na sua própria linha, para facilitar a ordenação e adicionar novos atributos.
- Ao invés de colocar o texto diretamente em `android:text`, considere usar os [atributos designtime](http://tools.android.com/tips/layout-designtime-attributes) disponíveis para Android Studio.
```xml
<?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>
```
Regra geral, atributos `android:layout_****` devem ser definidos no layout XML, ao passo que outros atributos `android:****` devem ficar num XML de estilo. Esta regra tem excepções, mas de forma geral funciona bem. A ideia é manter apenas o layout (posicionamento, margem, tamanho) e atributos de conteúdo nos ficheiros de layout, mantendo todos os detalhes de aparência (cores, espaçamento, tipo de letra) em ficheiros de estilo.
As excepções são:
- `android:id` deverá ficar obviamente no ficheiro de layout
- `android:orientation` para um `LinearLayout` normalmente faz mais sentido num ficheiro de layout
- `android:text` deverá ficar no ficheiro de layout porque ele define conteúdo
- Por vezes irá fazer sentido definir um estilo genérico `android:layout_width` e `android:layout_height` mas por definição estes deverão aparecer no ficheiro de layout
**Use estilos.** Quase todos os projeto precisam de usar estilos de forma adequada, isto porque é muito comum ter de repetir a aparência de uma view. No mínimo deverá ter um estilo comum para a maioria do conteúdo de texto na sua aplicação, por exemplo:
```xml
<style name="ContentText">
<item name="android:textSize>@dimen/font_normal</item>
<item name="android:textColor">@color/basic_black</item>
</style>
```
Aplicado às TextViews:
```xml
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/price"
style="@style/ContentText"
/>
```
Provavelmente irá ter de fazer o mesmo para botões, mas não pare já por aqui. Vá mais além e mova um grupo de atributos `android:****` que estejam relacionados e sejam repetidos para um estilo comum.
**Divida um ficheiro de estilo grande em outros ficheiros.** Não precisa de ter apenas um ficheiro `styles.xml`. A Android SDK suporta outros ficheiros, e não há nada de mágico acerca do nome `styles`, o que importa são as tags de XML `<style>` dentro do ficheiro. Assim poderá ter os ficheiros `styles.xml`, `styles_home.xml`, `styles_item_details.xml`, `styles_forms.xml`. Ao contrário dos nomes dos diretório de recursos que têm algum significado para o sistema de compilação, nomes de ficheiros dentro de `res/values` podem ser arbitrários.
**`colors.xml` é uma palete de cores.** Não deverá existir mais nada no seu `colors.xml` do que apenas um mapeamento do nome da cor a um valor RGBA. Não o use para definir valores RGBA para diferentes tipos de botões.
*Não faça isto:*
```xml
<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>
```
Pode facilmente começar a repetir valores RGBA desta forma, e isso faz com que seja mais difícil alterar uma cor básica se isso for necessário. Além disso, essas definições estão relacionadas a algum contexto, como pode exemplo "button" ou "comment", e devem ficar no estilo do botão, não em `colors.xml`.
Ao invés, faça isto:
```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>
```
Peça esta palete ao designer da aplicação. Os nomes não precisam de ser nomes de cores como "green", "blue", etc. Nomes como "brand_primary", "brand_secondary", "brand_negative" são também totalmente aceites. Formatar as cores desta forma irá fazer com que as alterar ou fazer a refatorização de cores seja mais fácil, e também irá mostrar de forma explícita as diferentes cores que estão a ser utilizadas. Normalmente para um UI com bom aspeto, é importante reduzir a variedade das cores que estão a ser utilizadas.
**Trate o dimens.xml como o colors.xml.** Deverá também definir uma "palete" de espaçamentos e tamanhos de letra típicos, devido basicamente às mesmas razões que para as cores. Um bom exemplo de um ficheiro 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>
```
Nas margens e espaçamentos deverá usar as dimensões `spacing_****` para o layout, ao invés de colocar valores fixos, da mesma forma que o strings são tratados. Isto irá dar um look-and-feel consistente, ao mesmo tempo que irá fazer com que seja mais fácil organizar e modificar estilos e layouts.
**strings.xml**
Dê o nome aos strings com keys que fazem lembrar namespaces, e não tenha receio de repetir um valor para duas ou mais keys. Linguagens são complexas, portanto namespaces são necessários para trazer contexto e quebrar ambiguidade.
**Ruim*
```xml
<string name="network_error">Erro de rede</string>
<string name="call_failed">Pedido falhou</string>
<string name="map_failed">Carregamento do mapa falhou</string>
```
**Bom**
```xml
<string name="error.message.network">Erro de rede</string>
<string name="error.message.call">Pedido falhou</string>
<string name="error.message.map">Carregamento do mapa falhou</string>
```
Não escreva os valores do strings em letras maiúsculas. Mantenha-se dentro das convenções normais de texto (exemplo: coloque o primeiro caráter em letras maiúscula). Se precisa de mostrar o string em letras maiúsculas, então use por exemplo o atributo [`textAllCaps`](http://developer.android.com/reference/android/widget/TextView.html#attr_android:textAllCaps) na própria TextView.
**Ruim**
```xml
<string name="error.message.call">PEDIDO FALHOU</string>
```
**Bom**
```xml
<string name="error.message.call">Pedido falhou</string>
```
**Evite uma hierarquia muito profunda de views.** Por vezes poderá ser tentado a adicionar apenas mais uma LinearLayout, para conseguir organizar as suas views. Este tipo de situação pode ocorrer:
```xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<RelativeLayout
...
>
<LinearLayout
...
>
<LinearLayout
...
>
<LinearLayout
...
>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
```
Mesmo que não veja isso explicitamente num ficheiro de layout, poderá na mesma acontecer se está a inflando (em Java) views noutras views.
Um par de problemas pode ocorrer. Poderá experimentar problemas de performance, porque há uma árvore complexa de UI que o processador precisa de gerir. Outro problema mais sério é a possibilidade de [StackOverflowError](http://stackoverflow.com/questions/2762924/java-lang-stackoverflow-error-suspected-too-many-views).
Por isso, tente manter a hierarquia das views tão lisa quanto possível: aprenda como usar [RelativeLayout](https://developer.android.com/guide/topics/ui/layout/relative.html), como [optimizar os seus layouts](http://developer.android.com/training/improving-layouts/optimizing-layout.html) e como usar a [tag `<merge>`](http://stackoverflow.com/questions/8834898/what-is-the-purpose-of-androids-merge-tag-in-xml-layouts).
**Esteja atento a problemas relacionados com WebViews.** Quando tem de mostrar uma página web, por exemplo para uma notícia, evite usar o processamento no cliente para limpar o HTML, ao invés, peça um HTML "*puro*" aos programadores do servidor. [WebViews também podem vazar memória](http://stackoverflow.com/questions/3130654/memory-leak-in-webview) quando mantêm uma referência à sua Activity, ao invés de conetada ao ApplicationContext. Evite usar uma WebView para textos ou botões simples, dê preferência às TextViews ou Buttons.
### Frameworks para testes
As frameworks de teste do Android SDK ainda estão na sua infância, especialmente no que toca aos testes UI. O Android Gradle currentemente implementa uma tarefa de teste chamada [`connectedAndroidTest`](http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Testing), a qual corre testes JUnit criados por si, usando uma extensão de JUnit com ajudantes para Android](http://developer.android.com/reference/android/test/package-summary.html). Isto significa que irá ter de correr os testes conectado a um aparelho, ou um simulador. Siga o guia oficial [[1]](http://developer.android.com/tools/testing/testing_android.html) [[2]](http://developer.android.com/tools/testing/activity_test.html) para testar
**Use [Robolectric](http://robolectric.org/) apenas para os testes unitários, não para as views.**
É uma framework de teste que procura testar de forma "desconectada de um aparelho" para melhorar a velocidade, desenhado especialmente para os testes unitários aos modelos e _view models_. Contudo, testar sobe o Robolectric é pouco fiável e incompleto no que toca a testes UI. Irá ter problemas a testar elementos UI relacionados com animações, janelas de diálogo, etc., e isto irá ainda ficar mais complicado porque está a "caminhar no escuro" (testando sem ver o ecrã que está a ser controlado).
**[Robotium](https://code.google.com/p/robotium/) faz com que escrever testes UI seja fácil.** Não precisa do Robotium para correr testes conectados em casos de UI, mas irá provavelmente ser benéfico para si porque ele tem muitos ajudantes para obter e analisar as views, e controlar o ecrã. Casos de teste irão ser tão simples como:
```java
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"));
```
### Emuladores
A performance do emulador do Android SDK, particularmente a de arquitetura x86, tem melhorado significantemente nos últimos anos e hoje é a mais adequada para a maioria das tarefas de desenvolvimento do dia-a-dia. Contudo, você não deve desconsiderar que sua aplicação vai funcionar realmente em dispositivos reais. Claro, testar todos os possíveis dispositivos não é nada prático ou produtivo, ou seja, foque os seus esforços nos dispositivos que tenham uma grande fatia de mercado e os que sejam mais relevantes para a aplicação que está sendo desenvolvida no momento.
### Configurações Proguard
[ProGuard](http://proguard.sourceforge.net/) é normalmente usado em projetos Android para diminuir ou ofuscar o código empacotado.
Quer esteja a usar o ProGuard que não depende da configuração do seu projeto. Frequentemente irá configurar o gradle para usar ProGuard quando compilando a apk de lançamento.
```groovy
buildTypes {
debug {
minifyEnabled false
}
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
```
De maneira a determinar que partes do código têm de ser preservadas e quais podem ser descartadas ou ofuscadas, terá de especificar um ou mais pontos de entrada para o seu código. Estes pontes de entrada são normalmente classes com main métodos, applets, midlets, atividades, etc.
A framework Android usa uma configuração padrão que pode ser encontrada em `SDK_HOME/tools/proguard/proguard-android.txt`. Usando a configuração acima, regras ProGuard específicas para o projeto, definidas em `my-project/app/proguard-rules.pro`, serão anexadas à configuração padrão.
Um problema comum relacionado com o ProGuard é verificar se a aplicação tem um crash ao iniciar com `ClassNotFoundException` ou `NoSuchFieldException` ou similar, mesmo que o comando de compilação (i.e. `assembleRelease`) seja bem sucedido sem qualquer aviso.
Isto significa uma de duas coisas:
1. ProGuard removou a classe, enum, método, nome do campo ou anotação, considerando que esta não era necessária.
2. ProGuard ofuscou (deu outro nome) à classe, enum ou nome do campo, mas continua a ser usada indiretamente pelo seu nome original, i.e. através da reflexão do Java.
Verifique `app/build/outputs/proguard/release/usage.txt` para ver se o objeto em questão foi removido.
Verifique `app/build/outputs/proguard/release/mapping.txt` para ver se o objeto em questão foi ofuscado.
De maneira a prevenir o ProGuard de *remover* classes ou membros da classe que sejam necessários, adicione a opção `keep` à sua configuração do ProGuard:
```
-keep class com.futurice.project.MyClass { *; }
```
De maneira a prevenir o ProGuard de *ofuscar* classes ou membros da classe, adicione `keepnames`
```
-keepnames class com.futurice.project.MyClass { *; }
```
Leia mais em [Proguard](http://proguard.sourceforge.net/#manual/examples.html) para exemplos.
**Cedo no projeto, faça uma compilação de lançamento** para verificar se as regras do ProGuard estão a manter corretamente o que é importante. Além disso, sempre que inclua bibliotecas novas, faça uma compilação de lançamento e teste a apk num aparelho. Não espera até a sua aplicação estar finalmente na versão "1.0" para fazer a compilação de lançamento, poderá encontrar algumas surpresas desagradáveis e pouco tempo para as resolver.
**Dica.** Guarde o ficheiro `mapping.txt` por cada lançamento que publique para os seus utilizadores. Ao guardar uma cópia do ficheiro `mapping.txt` por cada compilação de lançamento, assegura-se de que poderá fazer o debug de um problema se o utilizador encontrar um bug e submeter um stack trace ofuscado.
**DexGuard**. Se necessita de ferramentas hard-core para optimizar, e especialmente ofuscar código de lançamento, considere o [DexGuard](http://www.saikoa.com/dexguard), um software comercial feito pela mesma equipa do ProGuard. Também poderá dividir ficheiros Dex para resolver o problema da limitação dos 65 mil métodos.
### Armazenamento de dados
#### SharedPreferences
Se precisa de persistir apenas flags simples e a sua aplicação corre em apenas um processo, SharedPreferences é muito provavelmente suficiente para si. É uma boa opção padrão.
Há duas razões que o podem levar a não querer usar SharedPreferences:
* *Performance*: Os seus dados são complexos ou simplesmente há muitos dados a serem guardados
* *Vários processos querendo aceder aos dados*: Você tem widgets ou serviços remotos que são executados em seus próprios processos e exigem dados sincronizados
#### ContentProviders
No caso de que as SharedPreferences não são suficientes para si, deverá usar
o padrão da plataforma ContentProviders, que são rápidos e seguros no uso de processos.
O único problema com ContentProviders é a quantidade de código boilerplate que é necessário para configurá-los, bem como tutoriais de baixa qualidade. É possível, contudo, gerar ContentProvider utilizando bibliotecas como [Schematic](https://github.com/SimonVT/schematic), que reduz significativamente o esforço.
Você ainda precisa escrever algum código de parsing você mesmo para ler os objetos de dados das colunas do SQLite e vice-versa. É possível serializar os objetos de dados, por exemplo, com Gson, e fazer persistir apenas a string resultante. Desta forma, você perde em performance, mas por outro lado não precisa de declarar uma coluna para cada um dos campos da classe de dados.
#### Usando um ORM
Nós geralmente não recomendamos o uso de uma biblioteca de Object-Relation Mapping, a não ser que tenha dados invulgarmente complexos e uma necessidade extrema. Eles tendem a ser complexos e necessitam de tempo para aprender. Se decidir avançar com um ORM deverá prestar atenção para saber se é ou não é _seguro em processos_ se a sua aplicação o exige, visto que muitas das soluções ORM existentes neste momento surpreendentemente não o são.
### Obrigado a
Antti Lammi, Joni Karppinen, Peter Tackage, Timo Tuominen, Vera Izrailit, Vihtori Mäntylä, Mark Voit, Andre Medeiros, Paul Houghton e outros programadores da Futurice por partilharem o seu conhecimento em desenvolvimento Android.
### License
[Futurice Oy](http://www.futurice.com)
Creative Commons Attribution 4.0 International (CC BY 4.0)
================================================
FILE: translations/Russian/README.ru.md
================================================
# Лучшие практики в Android разработке
Уроки от лучших Android-разработчиков из команды Futurice. Не нужно изобретать велосипед, просто следуйте правилам в руководстве. Если вы интересуетесь разработкой для IOS или Windows Phone, обратите внимание на [**iOS Good Practices**](https://github.com/futurice/ios-good-practices) и [**Windows App Development Best Practices**](https://github.com/futurice/windows-app-development-best-practices).
[](https://android-arsenal.com/details/1/1091)
[](https://spiceprogram.org/)
## Cодержание
#### [Используйте Gradle и рекомендованную им структуру проекта](#build-system)
#### [Храните пароли и важные данные в файле gradle.properties](#gradle-configuration)
#### [Используйте библиотеку Jackson для парсинга JSON данных](#libraries)
#### [Не пишите свой HTTP клиент, используйте библиотеки Volley или OkHttp](#networklibs)
#### [Не нужно использовать Guava и большое количество библиотек чтобы не привысить лимит методов (65k)](#methodlimitation)
#### [Используйте фрагменты для отображения пользовательского интерфейса](#activities-and-fragments)
#### [Используйте activity только для управления фрагментами](#activities-and-fragments)
#### [XML-разметка — это код, который нужно писать аккуратно](#resources)
#### [Чтобы не дублировать атрибуты в XML-разметке, используйте стили](#styles)
#### [Используйте несколько файлов со стилями, не нужно создавать один огромный файл](#splitstyles)
#### [Сделайте файл colors.xml компактным. Не дублируйте цвета, задайте основную палитру](#colorsxml)
#### [То же касается и dimens.xml, задайте только основные константы](#dimensxml)
#### [Избегайте слишком глубокой иерархии элементов ViewGroup](#deephierarchy)
#### [Не нужно обрабатывать WebView на клиентской стороне, будьте внимательны к возможным утечкам](#webviews)
#### [Используйте Robolectric для unit-тестов, а Robotium для UI-тестов](#test-frameworks)
#### [Используйте Genymotion в качестве эмулятора](#emulators)
#### [Всегда используйте ProGuard или DexGuard](#proguard-configuration)
#### [Для простого хранения данных используйте SharedPreferences, для сложной структуры данных - ContentProvider](#data-storage)
#### [Используйте Stetho для отладки приложения](#use-stetho)
#### [Используйте Leak Canary для поиска утечек памяти](#use-leakcanary)
#### [Используйте CI](#use-continuous-integration-1)
----------
### Android SDK
Разместите [Android SDK](https://developer.android.com/sdk/installing/index.html?pkg=tools) в домашней дирректории или другом месте, не связанном с приложением. Некоторые IDE включают в себя SDK при установке и могут размещать его в своей дирректории. Это не совсем правильно, так как в дальнейшем вам может понадобиться обновить, переустановить или сменить IDE. Не размещайте SDK в системные папки, так как для доступа к ним может понадобиться ипользование команды sudo, если вы вошли в систему как обычный пользователь.
### Система сборки
<a name="build-system"></a>
Используйте [Gradle](http://tools.android.com/tech-docs/new-build-system) по умолчанию. У Ant меньше возможностей и его код менее компактный. Используя Gradle, вы сможете:
- Создавать различные сборки и варианты приложения
- Создавать простые задачи в виде скрипта
- Управлять и загружать зависимости
- Настраивать хранилище ключей
- И многое другое
Плагин Gradle находится в активной разработке командой Google и уже стал основной системой сборки для Android
### Структура проекта
Существует два варианта структуры проекта: старый 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
```
Главным отличием является то, что новая структура явно разделяет 'наборы ресурсов' (`main`, `androidTest`), используя одну из концепций Gradle. Например, вы можете добавить папки 'paid' и 'free' в вашу папку `src`, в которых будет отдельный исходный код для платной и бесплатной версий приложения.
Наличие папки `app` в верхнем уровне иерархии помогает отделить ваше приложение от библиотек (например, `library-foobar`), которые в нем используются. В таком случае файл `settings.gradle` хранит проекты, на которые может ссылаться `app/build.gradle`.
### Конфигурация Gradle
<a name="gradle-configuration"></a>
**Общая структура.** Следуйте [Рекомендациям Google для Gradle for Android](http://tools.android.com/tech-docs/new-build-system/user-guide)
**Маленькие задачи.** Вместо скриптов на shell, Python, Perl, вы можете создавать задачи в Gradle. Подробне в документации [Документация Gradle](http://www.gradle.org/docs/current/userguide/userguide_single.html#N10CBF).
**Пароли.** В файле `build.gradle` вашего приложения необходимо задать `signingConfigs` для релизной сборки. Here is what you should avoid:
_Не делайте так_. Эта информаци появится в системе контроля версий.
```groovy
signingConfigs {
release {
storeFile file("myapp.keystore")
storePassword "password123"
keyAlias "thekey"
keyPassword "password789"
}
}
```
Вместо этого, создайте файл `gradle.properties`, который _не_ будет добавлен в систему контроля версий:
```
KEYSTOR
gitextract_mcniy78j/
├── LICENSE
├── README.md
└── translations/
├── Chinese/
│ └── README.cn.md
├── French/
│ └── README.fr.md
├── Japanese/
│ └── README.ja.md
├── Korean/
│ └── README.ko.md
├── Portuguese/
│ └── README.pt.md
├── Russian/
│ └── README.ru.md
├── Spanish/
│ └── README.es.md
├── Turkish/
│ └── README.tr.md
└── Vietnamese/
└── README.vi.md
Condensed preview — 11 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (357K chars).
[
{
"path": "LICENSE",
"chars": 16620,
"preview": "Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice."
},
{
"path": "README.md",
"chars": 37482,
"preview": "# Best practices in Android development\n\nAvoid reinventing the wheel by following these guidelines. Lessons learned from"
},
{
"path": "translations/Chinese/README.cn.md",
"chars": 20518,
"preview": "# Android 开发最佳实践\n\n从[Futurice](http://www.futurice.com)公司Android开发者中学到的经验。\n遵循以下准则,避免重复发明轮子。若你对开发iOS或Windows Phone 有兴趣,\n请看"
},
{
"path": "translations/French/README.fr.md",
"chars": 35248,
"preview": "# Les bonnes pratiques du développement Android\n\nLeçons apprises par les developpeurs Android de [Futurice](http://www.f"
},
{
"path": "translations/Japanese/README.ja.md",
"chars": 20855,
"preview": "# Androidの開発におけるベストプラクティス\n\n以下は[Futurice](http://www.futurice.com)で働くAndroidアプリ開発者が学んだ教訓である. これらをしっかり読んで車輪の再開発はやめよう. もしiO"
},
{
"path": "translations/Korean/README.ko.md",
"chars": 24112,
"preview": "# Android 개발 모범 사례\n\n다음은 [Futurice](http://www.futurice.com)의 Android 개발자들로부터 학습한 내용들이다. 이 가이드를 따라가면서 이미 했던 작업을 되풀이(Reinv"
},
{
"path": "translations/Portuguese/README.pt.md",
"chars": 37064,
"preview": "# As melhores práticas no desenvolvimento Android\n\nLições aprendidas por programadores Android na [Futurice](http://www."
},
{
"path": "translations/Russian/README.ru.md",
"chars": 42815,
"preview": "# Лучшие практики в Android разработке\n\nУроки от лучших Android-разработчиков из команды Futurice. Не нужно изобретать в"
},
{
"path": "translations/Spanish/README.es.md",
"chars": 40234,
"preview": "# Buenas prácticas en el desarrollo de Android\n\nÉstas son algunas de las lecciones aprendidas por los desarrolladores de"
},
{
"path": "translations/Turkish/README.tr.md",
"chars": 37397,
"preview": "# Android Geliştirmede En İyiyi Yakalamak\n\n[Futurice](http://www.futurice.com)'deki Android Geliştiriciler tarafından öğ"
},
{
"path": "translations/Vietnamese/README.vi.md",
"chars": 36486,
"preview": "# Những phương pháp tốt nhất trong việc phát triển Android (Best practices in Android development)\n\nĐây là những bài học"
}
]
About this extraction
This page contains the full source code of the futurice/android-best-practices GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 11 files (340.7 KB), approximately 95.3k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.