Do's and Don'ts for Android development, by Futurice developers

Last update: Jun 24, 2022

Best practices in Android development

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

Android Arsenal Spice Program Sponsored

Summary

Use Gradle and its default project structure

Put passwords and sensitive data in gradle.properties

Use the Jackson library to parse JSON data

Don't write your own HTTP client, use OkHttp libraries

Avoid Guava and use only a few libraries due to the 65k method limit

Sail carefully when choosing between Activities and Fragments

Layout XMLs are code, organize them well

Use styles to avoid duplicate attributes in layout XMLs

Use multiple style files to avoid a single huge one

Keep your colors.xml short and DRY, just define the palette

Also keep dimens.xml DRY, define generic constants

Do not make a deep hierarchy of ViewGroups

Avoid client-side processing for WebViews, and beware of leaks

Use JUnit for unit tests, Espresso for connected (UI) tests, and AssertJ-Android for easier assertions in your Android tests

Always use ProGuard or DexGuard

Use SharedPreferences for simple persistence, otherwise ContentProviders

Use Stetho to debug your application

Use Leak Canary to find memory leaks

Use continuous integration


Android SDK

Place your Android SDK 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 using the Android Gradle plugin.

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 as this simplify your build scripts.

Gradle configuration

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

minSdkVersion: 21 We recommend to have a look at the Android version usage chart 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 for more details. Google also provides some helpful Gradle recipes, 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.

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:

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:

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

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 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 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 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 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, another of Square's open source libraries, builds upon learnings from the development of Gson and also integrates well with Kotlin.

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 for efficient HTTP requests and using Retrofit to provide a typesafe layer. If you choose Retrofit, consider 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 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], [2], [3], [4]. For a reference app, our open source app 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 for Android threading support and RxBinding to easily create Observables from existing Android components.

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.

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] [2] [3]. 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 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, 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), and fragments might be used solely as controllers. 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 extensively, because matryoshka bugs 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 available for Android Studio.
<?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

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:

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

Applied to TextViews:

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

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.

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:

<resources>
    <color name="button_foreground">#FFFFFF</color>
    <color name="button_background">#2A91BD</color>
</resources>    

Instead, do this:

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

<color name="button_foreground">@color/white</color> 
<color name="button_background">@color/blue</color> 

Then in styles.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.

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:

<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

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

Good

<string name="error_message_network">Network error</string>
<string name="error_message_call">Call failed</string>
<string name="error_message_map">Map loading failed</string>

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 on a TextView.

Bad

<string name="error_message_call">CALL FAILED</string>

Good

<string name="error_message_call">Call failed</string>

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:

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

Therefore, try to keep your views hierarchy as flat as possible: learn how to use ConstraintLayout, how to optimize your layouts and to use the <merge> tag.

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 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 for unit testing Plain, Android dependency-free unit testing on the JVM is best done using Junit.

Avoid Robolectric 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 makes writing UI tests easy.

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:

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

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 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, 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, 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 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 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 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 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 and Checkstyle are tools that ensure the code quality while Findbugs 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 is a good option if you have a local server at your disposal, on the other hand Travis CI 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

 logo

This project is sponsored by Spice Program, our open source and social impact program made with love by Futurice.

License

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

GitHub

https://github.com/futurice/android-best-practices
Comments
  • 1. Make it easier to start using the template: do not fail build if KEYSTORE_PASSWORD & KEY_PASSWORD are missing

    Update: this PR just replaces throwing an exception with printing a warning; see https://github.com/futurice/android-best-practices/pull/49#issuecomment-82822853


    I had too much trouble opening newly cloned rx-architecture project in Android Studio; it failed with "You should define KEYSTORE_PASSWORD and KEY_PASSWORD in gradle.properties." Setting defaults in gradle.properties makes it easier for people to actually start using the template.

    Reviewed by jonikarppinen at 2015-03-13 10:57
  • 2. Roboelectric for unit testing?

    I was in the Droidcon a couple of days ago and there was a talk about why not to use Roboelectric. Basically what he said is that it makes the tests slow and when you use it only for unit testing and not for views (what we recommend), doesn't provide anything you could not do yourself.

    I don't have experience with Robolectric myself, I never needed it, if my class uses some Bundle or framework related stuff I just wrap it.

    So my question: is really a good practice to use Robolectric when it makes your tests slow and it is possible to live without it?

    Reviewed by luciapayo at 2016-06-18 16:32
  • 3. Some questions

    Hello. I just have some questions/remarks.

    Use Gradle and its recommended project structure

    Why not Maven?

    Don't write your own HTTP client, use Volley or OkHttp libraries

    Aquery, Android Asynchronous Http Client?

    Use the Jackson library to parse JSON data

    Not GSON?

    Do not make a deep hierarchy of ViewGroups

    Mb add some links, like this

    There are two popular options: the old Ant & Eclipse ADT project structure, and the new Gradle & Android Studio project structure.

    Maven & Eclipse?

    Always use ProGuard or DexGuard

    Shrink? Penny-wise and pound-foolish. Obfuscate? Not sure. Is it always necessary?

    Reviewed by Suvitruf at 2015-07-23 11:01
  • 4. Add recommendation to store sensitive data (API keys and URLs, tracki…

    …ng id's, etc) in build.gradle & grade.properties

    By storing sensitive data (API keys and URLs, tracking ids, etc) in build.gradle & gradle.properties files instead of config.xml file that can be easily decompiled we ensure that sensitive data is stored securely and will not be compromised. Additional advantage is that you can access those constants with BuildConfig.GOOGLE_API_KEY where Context is not available.

    Reviewed by iverc at 2017-05-03 16:18
  • 5. Recommendation on the use of support library

    • Should one use the support library?
    • When does it make sense to use the support library?
    • What are the advantages of using support fragments vs normal fragments (a.k.a android.app.Fragment vs android.support.v4.app.Fragment)?

    The answer(s) to the above question might make a lot of sense in an android best practices doc?

    Reviewed by kaushikgopal at 2014-11-06 19:48
  • 6. [Talk] Activities and Fragments are controllers

    Hi,

    I am a web MVC programmer and I developed several projects with Android and Android Studio, i consider the basic architecture as not MVC since Controllers and View are mixed. Separating layout in another file is not enough to make it MVC compliant, because you need some code processing this layout. This is why for some needs, i created another concept, the LayoutView, this is specific to one layout and allow developer to interact between the controller (Activity/Fragment) and the Template (layout.xml). The layout's LayoutView inherits from an interface (provided by the controller).

    This allow multiple layout for one controller depending on usage and allowing reusage through your different apps. Here is an example for a login form, the known layout are commonly, basic or featured:

    public interface LayoutView {
    
        public int getLayoutID();
    
    }
    
    public interface LoginView extends com.another.package.views.LayoutView {
        public void reset();
    
        public void setUp();
    
        Credentials getLoginForm();
    
        public void onLoginStart();
    
        public void onLoginEnd();
    
        public void onLoginSuccess();
    
        public void onLoginError();
    }
    

    getLayoutID() returns the layout ID \o/ setUp() is called by onCreate() or onCreateView() reset() is called by onResume() getLoginForm() is called when clicking Login button, this method extract Credentials from form. Other method provide a way to show the state of the request to the user (here a Rest API request with Retrofit).

    The architecture is

    • controllers
      • activities
      • fragments
      • adapters
    • views
      • LoginView
      • BasicLoginView
      • FeaturedLoginView
    • models
      • User

    This system is not yet mature.

    What do you think about it ?

    Reviewed by Cartman34 at 2015-12-17 10:08
  • 7. Avoiding Guava

    The guidelines say "avoid using the Guava library, since it contains over 13k methods", as part of the guidance for dealing with the dex method limitation.

    While avoiding the method limits is definitely a useful point, I'm pretty suspect of Guava as an issue itself.

    For starters, because it's so heavily used in the Android ecosystem already: http://www.appbrain.com/stats/libraries/details/guava/google-guava. It's in 6% of the 500 most downloaded apps, including Spotify, Tumblr, etc.

    On top of that, I think Proguard will strip out any methods you don't end up actually using there, so you won't get all 13K methods anyway.

    Is there something else I'm missing? It seems like largeish libraries (like Guava) shouldn't actually be an issue, it's more a problem you hit if your app itself grows too large.

    Oh, and top of that you should probably put in a note about multi-dex now (or the reasons not to use that I guess, I don't know): http://developer.android.com/tools/building/multidex.html. As of very recently, Google now support building apps with multiple dex files, to solve this issue entirely (and a support library too, to backport that)

    Reviewed by pimterry at 2014-11-15 12:04
  • 8. Using an event bus for communication

    Using an event bus is excellent for decoupling communication between Fragments and also between Services and Activities. Possible mechanisms:

    • Otto (my personal recommendation)
    • Green Robot EventBus
    • RxJava

    I'm guessing the developers of Futurice are obviously aware of the use of event bus but chose not to include it in this list for a reason? If that's the case, I would love to hear an opinion on this.

    Reviewed by kaushikgopal at 2014-10-29 19:25
  • 9. Project structure: by-type vs. by-feature

    In my current projects I always choose by feature project structure grouping like

    • features
      • home
        • HomeView
        • HomePresenter
        • HomeAdapter
      • detail
      • etc
    • utils

    But in your recommendation you only mention by type and for clean code architectures is not clean at all, so I propose to add by feature project structure recommendation This is specially useful for MV* projects

    Reviewed by FireZenk at 2016-12-09 10:24
  • 10. Feature: Add assertion test framework best practice for Android using AssertJ-Android

    @futurice Hey there! I have added best practices on asserting Android specific components in Android tests. Let me know what you like and your reasoning if you don't. Thanks!

    Reviewed by ghost at 2016-11-06 18:13
  • 11. Is the MVP architecture better than MVC?

    Hi, I'm a beginner of Android and translating the doc to Chinese for few days since I noticed that you updated it.

    And I found a question when I translated this part https://github.com/futurice/android-best-practices#java-packages-architecture.

    I read some articles about the architecture of Android and found that more and more people suggest to use MVP architecture. So I wonder know if the MVC is still the best choice now?

    Reviewed by santong at 2015-11-10 03:33
  • 12. README.cn.md

    Android 开发最佳实践

    //Add version of android pls 🩹

    Futurice公司Android开发者中学到的经验。 遵循以下准则,避免重复发明轮子。若你对开发iOS或Windows Phone 有兴趣, 请看iOS Good PracticesWindows client 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放在你的home目录或其他应用程序无关的位置。 当安装有些包含SDK的IDE的时候,可能会将SDK放在IDE同一目录下,当你需要升级(或重新安装)IDE或更换的IDE时,会非常麻烦。 此外,如果你的IDE是在普通用户下运行,而不是在root下运行,还要避免把SDK放到一下需要sudo权限的系统级别目录下。

    构建系统

    你的默认编译环境应该是Gradle. 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

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

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

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

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

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

    KEYSTORE_PASSWORD=password123
    KEY_PASSWORD=password789
    

    那个文件是gradle自动引入的,你可以在buld.gradle文件中使用,例如:

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

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

    dependencies {
    	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,因为他是由谷歌开发,很好地支持Gradle,包含很多有用的检测和分析工具,默认使用最新的工程结构,它就是为Android开发定制的。

    你也可以使用纯文版编辑器如Vim,Sublime Text,或者Emacs。如果那样的话,你需要使用Gradle和adb命令行。

    不再推荐使用Eclipse和ADT开发,因为谷歌在2015年年末结束了对ADT的支持,并呼吁开发者尽快迁移到Android Studio。

    无论你使用何种开发工具,避免将你的编辑器配置文件(比如Android Studio的iml文件)加入到版本控制,因为这些文件通常包含与本地机器有关的配置,可能会影响你的同事。

    最后,善待其他开发者,不要强制改变他们的开发工具和偏好。

    类库

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

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

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

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

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

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

    同时在每个module 的build.gradle中添加

    apply plugin: 'retrolambda'
    
    android {
    	compileOptions {
    	sourceCompatibility JavaVersion.VERSION_1_8
    	targetCompatibility JavaVersion.VERSION_1_8
    }
    
    retrolambda {
    	jdk System.getenv("JAVA8_HOME")
    	oldJdk System.getenv("JAVA7_HOME")
    	javaVersion JavaVersion.VERSION_1_7
    }
    

    Android Studio 提供Java8 lambdas表带是代码提示支持。如果你对lambdas不熟悉,只需参照以下开始学习吧:

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

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

    Activities and Fragments

    Fragments应该作为你实现UI界面默认选择。你可以重复使用Fragments用户接口来 组合成你的应用。我们强烈推荐使用Fragments而不是activity来呈现UI界面,理由如下:

    • 提供多窗格布局解决方案 Fragments 的引入主要将手机应用延伸到平板电脑,所以在平板电脑上你可能有A、B两个窗格,但是在手机应用上A、B可能分别充满 整个屏幕。如果你的应用在最初就使用了fragments,那么以后将你的应用适配到其他不同尺寸屏幕就会非常简单。

    • 屏幕间数据通信 从一个Activity发送复杂数据(例如Java对象)到另外一个Activity,Android的API并没有提供合适的方法。不过使用Fragment,你可以使用 一个activity实例作为这个activity子fragments的通信通道。即使这样比Activity与Activity间的通信好,你也想考虑使用Event Bus架构,使用如 Otto 或者 greenrobot EventBus作为更简洁的实现。 如果你希望避免添加另外一个类库,RxJava同样可以实现一个Event Bus。

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

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

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

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

    Java 包结构

    Android 应用程序在架构上大致是Java中的Model-View-Controller结构。 在Android 中 Fragment和Activity通常上是控制器类(http://www.informit.com/articles/article.aspx?p=2126865). 换句话说,他们是用户接口的部分,同样也是Views视图的部分。

    正是因为如此,才很难严格的将fragments (或者 activities) 严格的划分成 控制器controlloers还是视图 views。 最还是将它们放在自己单独的 fragments 包中。只要你遵循之前提到的建议,Activities 则可以放在顶级目录下。 如果你规划有2到3个以上的activity,那么还是同样新建一个activities包吧。

    然而,这种架构可以看做是另一种形式的MVC, 包含要被解析API响应的JSON数据,来填充的POJO的models包中。 和一个views包来包含你的自定义视图、通知、导航视图,widgets等等。 适配器Adapter是在数据和视图之间。然而他们通常需要通过getView()方法来导出一些视图, 所以你可以将adapters包放在views包里面。

    一些控制器角色的类是应用程序级别的,同时是接近系统的。 这些类放在managers包下面。 一些繁杂的数据处理类,比如说"DateUtils",放在utils包下面。 与后端交互负责网络处理类,放在network包下面。

    总而言之,以最接近用户而不是最接近后端去安排他们。

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

    资源文件 Resources

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

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

    • 每一个属性一行,缩进4个空格
    • android:id 总是作为第一个属性
    • android:layout_**** 属性在上边
    • style 属性在底部
    • 关闭标签/>单独起一行,有助于调整和添加新的属性
    • 考虑使用Designtime attributes 设计时布局属性,Android Studio已经提供支持,而不是硬编码android:text (译者注:墙内也可以参考stormzhang的这篇博客链接)。
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
    	xmlns:android="http://schemas.android.com/apk/res/android"
    	xmlns:tools="http://schemas.android.com/tools"
    	android:layout_width="match_parent"
    	android:layout_height="match_parent"
    	android:orientation="vertical"
    	>
    
    	<TextView
    		android:id="@+id/name"
    		android:layout_width="match_parent"
    		android:layout_height="wrap_content"
    		android:layout_alignParentRight="true"
    		android:text="@string/name"
    		style="@style/FancyText"
    		/>
    
    	<include layout="@layout/reusable_part" />
    
    </LinearLayout>
    

    作为一个经验法则,android:layout_****属性应该在 layout XML 中定义,同时其它属性android:**** 应放在 styler XML中。此规则也有例外,不过大体工作 的很好。这个思想整体是保持layout属性(positioning, margin, sizing) 和content属性在布局文件中,同时将所有的外观细节属性(colors, padding, font)放 在style文件中。

    例外有以下这些:

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

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

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

    应用到TextView 中:

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

    你或许需要为按钮控件做同样的事情,不要停止在那里。将一组相关的和重复android:****的属性放到一个通用的style中。

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

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

    不要这样做

    <resources>
    	<color name="button_foreground">#FFFFFF</color>
    	<color name="button_background">#2A91BD</color>
    	<color name="comment_background_inactive">#5F5F5F</color>
    	<color name="comment_background_active">#939393</color>
    	<color name="comment_foreground">#FFFFFF</color>
    	<color name="comment_foreground_important">#FF9D2F</color>
    	...
    	<color name="comment_shadow">#323232</color>
    

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

    相反,这样做:

    <resources>
    
    	<!-- grayscale -->
    	<color name="white"     >#FFFFFF</color>
    	<color name="gray_light">#DBDBDB</color>
    	<color name="gray"      >#939393</color>
    	<color name="gray_dark" >#5F5F5F</color>
    	<color name="black"     >#323232</color>
    
    	<!-- basic colors -->
    	<color name="green">#27D34D</color>
    	<color name="blue">#2A91BD</color>
    	<color name="orange">#FF9D2F</color>
    	<color name="red">#FF432F</color>
    
    </resources>
    

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

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

    <resources>
    
    	<!-- font sizes -->
    	<dimen name="font_larger">22sp</dimen>
    	<dimen name="font_large">18sp</dimen>
    	<dimen name="font_normal">15sp</dimen>
    	<dimen name="font_small">12sp</dimen>
    
    	<!-- typical spacing between two views -->
    	<dimen name="spacing_huge">40dp</dimen>
    	<dimen name="spacing_large">24dp</dimen>
    	<dimen name="spacing_normal">14dp</dimen>
    	<dimen name="spacing_small">10dp</dimen>
    	<dimen name="spacing_tiny">4dp</dimen>
    
    	<!-- typical sizes of views -->
    	<dimen name="button_height_tall">60dp</dimen>
    	<dimen name="button_height_normal">40dp</dimen>
    	<dimen name="button_height_short">32dp</dimen>
    
    </resources>
    

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

    避免深层次的视图结构 有时候为了摆放一个视图,你可能尝试添加另一个LinearLayout。你可能使用这种方法解决:

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

    即使你没有非常明确的在一个layout布局文件中这样使用,如果你在Java文件中从一个view inflate(这个inflate翻译不过去,大家理解就行) 到其他views当中,也是可能会发生的。

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

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

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

    测试框架

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

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

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

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

    模拟器

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

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

    混淆配置

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

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

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

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

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

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

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

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

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

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

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

    更多例子请参考Proguard

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

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

    DexGuard 如果你需要核心工具来优化,和专门混淆的发布代码,考虑使用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解决方案都是非线程安全的。

    使用StethoStetho 是一个Facebook 开源的Android调试工具,它是Chrome Developer Tools的扩展。通过它可以检测应用的网络情况。它也允许你可以检测应用的数据库,shared preferences。但是,你应该确保Stetho只有在Debug状态下得以开启,而不是在正式发布版本中。

    使用LeakCanaryLeakCanary 是可以在应用运行中检测,定位内存泄露的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 Creative Commons Attribution 4.0 International (CC BY 4.0)

    Translation

    Translated to Chinese by andyiac

    Reviewed by Garciaa94 at 2021-06-05 05:29
  • 13. Managing dependencies

    Hello.

    I don't know about you but one of my least favorite part of programming is wasting time chasing down the right dependency magic string with the right version.

    There is a cool feature in recent versions of Gradle and Android Studio that are a huge improvment in this area and I wrote a Gradle plugin that makes it easy to leverage them. FYI if that's a topic you are interested in: https://github.com/jmfayard/buildSrcVersions

    Reviewed by jmfayard at 2018-12-27 08:22
  • 14. Jackson link is dead :)

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

    We should update it to https://github.com/FasterXML/jackson-docs :smiley:

    Reviewed by tormath1 at 2018-12-09 19:31
Android app built with MVP architectural approach and uses Marvel Comics API that allows developers everywhere to access information about Marvel's vast library of comics. :zap:
Android app built with MVP architectural approach and uses Marvel Comics API that allows developers everywhere to access information about Marvel's vast library of comics. :zap:

Villains & Heroes Android app built with MVP architectural approach and uses Marvel Comics API that allows developers everywhere to access information

Jun 28, 2021
An library to help android developers working easly with activities and fragments (Kotlin version)
An library to help android developers working easly with activities and fragments (Kotlin version)

AFM An library to help android developer working easly with activities and fragments (Kotlin) Motivation Accelerate the process and abstract the logic

Sep 3, 2020
Unity-Android-SDK-Plugins - Android SDK/Library/Plugins (aar) for Unity Developers

Unity Android SDK Plugins Unity Android SDK Plugins is an Open Source project th

Feb 13, 2022
Screenshot Kata for Android Developers with Kotlin. The main goal is to practice UI Screenshot Testing.
Screenshot Kata for Android Developers with Kotlin. The main goal is to practice UI Screenshot Testing.

KataScreenshot in Kotlin We are here to practice UI testing using screenshot tests for Android. We are going to use Espresso to interact with the Appl

Jan 28, 2022
Super Heroes Kata for Android Developers in Kotlin. The main goal is to practice UI Testing.
Super Heroes Kata for Android Developers in Kotlin. The main goal is to practice UI Testing.

KataSuperHeroes in Kotlin We are here to practice UI Testing. We are going to use Espresso to interact with the Application UI. We are going to use Ko

Feb 14, 2022
Forage-project - This is a project given by Google Android Developers team. It's specifically created for data persistance.

Forage - Starter Code Starter code for the fifth independent project for Android Basics in Kotlin. This project pairs with Unit 5 of Android Basics in

Jan 2, 2022
Mankgram is an Android application for sharing stories especially for developers.
Mankgram is an Android application for sharing stories especially for developers.

Mankgram is an Android application for sharing stories especially for developers. Mankgrams are created using the Kotlin programming language and built in Android Studio.

May 10, 2022
Changelog - a android library, it helps developers display the history of changes in their applications
Changelog - a android library, it helps developers display the history of changes in their applications

Changelog is a android library, it helps developers display the history of changes in their applications. Supports Locales, Layout direction

Apr 2, 2022
Maxibon kata for Kotlin Developers. The main goal is to practice property based testing.
Maxibon kata for Kotlin Developers. The main goal is to practice property based testing.

Kata Maxibon for Kotlin. We are here to practice property based testing. We are going to use KotlinTest to write our tests. We are going to practice p

Mar 18, 2021
TODO API Client Kata for Kotlin Developers. The main goal is to practice integration testing using MockWebServer
TODO API Client Kata for Kotlin Developers. The main goal is to practice integration testing using MockWebServer

KataTODOApiClient for Kotlin We are here to practice integration testsing using HTTP stubbing. We are going to use MockWebServer to simulate a HTTP se

Jan 13, 2022
An app for developers which contains more than 2.4k+ resources , with 1.2k+ free public API documentation
An app for developers which contains more than 2.4k+ resources , with 1.2k+ free public API documentation

ResourceUp We often spend a lot of time finding good resources to get started with our project right? ResourceUp aims to provide all useful resources

Apr 30, 2022
Jun 21, 2022
Taskify - An app to manage your daily tasks and boost your productivity. Taskify is built using kotlin and follows all modern android Development practices and hence is a good learning resource for beginners
Taskify - An app to manage your daily tasks and boost your productivity. Taskify is built using kotlin and follows all modern android Development practices and hence is a good learning resource for beginners

Taskify Taskify is an app to manage your daily tasks and boost your productivity Video Introduction ?? This is a small introduction video about Taskif

Jun 21, 2022
🎥 A Simple and Minimal Movies Android Application to demonstrate the Modern Android Development and Jetpack Compose.
🎥 A Simple and Minimal Movies Android Application to demonstrate the Modern Android Development and Jetpack Compose.

ComposeMovie Android ?? A Simple and Minimal Movies Android Application to demonstrate the Modern Android Development and Jetpack Compose. Built with

Apr 8, 2022
Aggregated Android news, articles, podcasts and conferences about Android Development
Aggregated Android news, articles, podcasts and conferences about Android Development

DroidFeed Curated news feed for Android Developers! Stay up to date with the latest Android Development news. Built for the Android developer communit

Jun 13, 2022
A Simple and Minimal Quotes Android Application to demonstrate the Modern Android Development tools
A Simple and Minimal Quotes Android Application to demonstrate the Modern Android Development tools

Quotee Android ?? A Simple and Minimal Quotes Android Application to demonstrate the Modern Android Development tools. Developed with ❤️ by Aminullah

Apr 8, 2022
A sample app illustrating Android development using Kotlin with MVVM architecture, Android Jetpack, and other commonly used libraries.

Anime Facts A sample app illustrating Android development using Kotlin with MVVM architecture, Android Jetpack, and other commonly used libraries. Ani

Dec 5, 2021
A simple Android project using modern Android development tools and libraries.
A simple Android project using modern Android development tools and libraries.

A simple Android project using modern Android development tools and libraries.

Feb 3, 2022