Giter Site home page Giter Site logo

android-best-practices's Introduction

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


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)

android-best-practices's People

Contributors

amouly avatar andyiac avatar brendankirby avatar brenopolanski avatar floftar avatar guillaumevalverde avatar ichigotake avatar izrailit avatar joan-domingo avatar jp1017 avatar kianoni avatar kypeli avatar luisramalho avatar markvoit avatar minsoopark avatar mizucoffee avatar pabloper avatar pedpess avatar peranikov avatar peter-tackage avatar quangctkm9207 avatar recuutus avatar salihgueler avatar scarletsky avatar staltz avatar stephanebg avatar stetro avatar tehmou avatar wcoder avatar zlyang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

android-best-practices's Issues

ORMs

You don't have any section speaking about DAO, ORM.
Best Practices, Library (Cupboar/OrmLite/ActiveAndroid/greenDAO/Ollie), ...

Services, package location recommendations ?

Using the package structure outlined in the document, what would be the recommended location for services ?

Its not really part of the view.

Create a pacakge under root called services ?

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

Writing strings in all uppercase

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.

I strongly disagree with that. Some devices (e.g. Samsung Galaxy Note 3 SM-N7505) do not really respect textAllCaps property of TextViews. So in order to make your app look exactly the same on all devices you SHOULD use string values in all uppercase.

Pro guard Optimize

Hello,

I would add in Proguard session that for Proguard really optimize the code you should change the line
proguardFiles getDefaultProguardFile('proguard-android.txt'), to proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), ( add -optimize into the file name), when added it, the release build will take longer bug you'll get a really reduced apk.

Not sure, but looks like the need to add the -optimize point to optimize file begin in new version of android studio or Proguard.

I had problems my self checking that Proguard was not doing what I was expecting, so I've found it.

Proguard advices

Maybe @markvoit or @jonikarppinen or both could share a few sentences about Proguard on the README.md ?

The idea isn't an introduction or tutorial-ish, but really advices coming from battle experiences. That's all what's needed.

(Always) Using Proguard

I would not say using Proguard for your Android application is always a good thing. Main reason why people use Proguard is to strip unused code from your .class files. Why would you do this. Mainly because either 1) You have a bloated library or 2) you have unused code in your own application. Both are scenarios that should be avoided in general, which does not yield into a good reason to use Proguard.

Another reason to use Proguard is obfuscation. But why would use that? For good security you should rely on encryption anyway.

So what's the reasoning for avoiding Proguard? Well, you can spend excess amounts of time tweaking its configuration file when maybe you wouldn't actually need to.

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)

Project templates

We currently have the branch templates which is getting ready with one template. As the name suggests, it will be a directory with templates (plural), more than one.

Currently we are making one template based on RxJava, inspired by Timo Tuominen's Rx Android architecture. But since RxJava is not everyone's cup of tea, we should also have templates without it.

There is also the Retrofit vs Volley dichotomy. I can see at least 3 strong and good project templates we should have:

  • RxJava + Retrofit + OkHttp + Picasso + Jackson
  • Retrofit + OkHttp + Picasso + Jackson
  • Volley + Jackson

We would some volunteer to guide the way with Volley, because at least I have no experience enough to be able to say "this is how you're supposed to use it".

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.

Small problem

A small problem in readME.The title,Development,not develoment.

Chinese version not update

I found last commit of Chinese version about 7 month ago and English version update some places from then on.

Question about Java packages architecture

I am wondering introducing fragments or adapters package might ask some classes or methods be unnecessarily public. Could you tell me why you think this packaging is best?

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

For example, if FragmentA takes advantage of an AdapterA and no other fragments do not use the adapter, it might be better they both are in the same package and their methods or classes (if necessary) are declared as package scope. However, if your package architecture is like above, they have to be public.

Do you think there are more classes or methods which would be shared among all the fragments or adapters? Or do you have any other pros for the architecture?

My current opinion is that it would be better to organize classes based on the screens they are related to. Of course, some might be shared among screens (e.g. custom views) but I think there would be less dependencies.

I have been bothering with Java packaging in Android development and I would appreciate if you could tell me some backgrounds behind this best practice.

Thank you.

Auto increment or revision numbers

Hello folks,

I am wondering what is the best practice to handle auto incrementation approach for your builds?
So far I am using approach of generating dynamic version names and code like this:

    android {
        defaultConfig {
            versionName = calculateVersionName()
            versionCode = calculateVersionCode()
        }
    }
    ext.calculateVersionName = {}
    ext.calculateVersionCode= {}

Inside project, I am storing version.properties file which is dynamically updated by script.
It basically looks like this:

#Mon Jun 08 11:12:29 UTC 2015
major=2
revision=11
minor=1
patch=0

Basic implementation of calculate methods is following:

Properties retrieveVersionProperties() {
    def properties = new Properties()
    def stream
    try {
        stream = new FileInputStream(versionFile)
        properties.load(stream)
    } catch (java.io.FileNotFoundException ignore) {
    } finally {
        if (stream != null) stream.close()
    }
    return properties
}

ext.calculateVersionName = {
    def version = retrieveVersionProperties()

    // safety defaults in case file missing
    if (!version['major']) version['major'] = "1"
    if (!version['minor']) version['minor'] = "0"
    if (!version['patch']) version['patch'] = "0"
    return "${major}.${minor}.${patch}"
}

ext.calculateVersionCode = {
    def version = retrieveVersionProperties()

    // safety defaults in case file missing
    if (!version['revision']) version['revision'] = "1"

    def code = 0
    code += Integer.valueOf(version['revision'])
    return code
}

So if I need to increment build I can simply update my version.properties file. Though I do not feel like this is the right way to handle auto increment.

Please, can you suggest me something elegant?

Cheers, Tom.

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?

Extend testing frameworks

Would be nice to have a section with best practices for using unit testing frameworks (JUnit, Mockito)

Problem with saving credentials into gradle.properties file

https://github.com/futurice/android-best-practices#gradle-configuration

For this part, when our project needs to be running on Continuous Integration server, which is automatically pulling code from Github, we need to be able to get those fake/dummy credentials to run the tests.

Is that better if you place all these credentials into Environment Variables list and export them accordingly when you're in your local machine or running them on CI servers.

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?

Project template example for ListViews and slightly more complicated views than TextView

The project template shows by way of the SubscriptionUtils class how one could bind data to a TextView.

I'm curious to see how you use this similar approach with list views, adapters and the view model. Do you use the subjects to publish results, and as the results are published, add them as single items to the list adapter?

Alternatively, do you publish a chunk of results for the list, and just set the items on the adapter with one swoop?

ViewPager inside ViewPager

Hi, anyone know if nested ViewPager can be a problem?
I've implemented this on my app and I have a huge increment of performace and non blocking UI, Because it avoid replace or add fragment every time when I need change the content.
I have created a NonSwipeableViewPager and added a normal ViewPager inside it. When the user select a item on NavigationDrawer, the NonSwipeableViewPager slide to position of the content.

Provider info

Hey guys, thanks for the tutorial and general guidelines. Do you have any best practice recommendations wrt providers?

Good practice on data set structuring

Hi, can anyone advise on how to create a good data set to be passed in the Adapter for View creation, like RecyclerView?

For instance, if we want to build a complex RecyclerView layout, how would the data set should look like in JSON. I know it is as simple as passing a Collection with multiple model classes and let the Adapter create the ViewType, but I'd like to know what are the best practices in doing this, scalability and performance wise.

Also, is it a good practice to pass multiple data set into an Adapter?

Thanks

Suggestion on reusable view/layouts & creating BaseActivity

Does reusing views/layouts a best practice? I've never done this before. Could anyone please advice on this matter?

I'd love to learn how to reuse my views/layouts or maybe even adapters. For now, I only create new layout for any listviews, gridviews, etc. Just with different file names to distinguish it all.

Edit: Just thought of this. Would it be possible if someone could give advice or tips on creating BaseActivity is a good practice or not, like what to put inside it? (navigation drawer in my mind right now). Perhaps, extending other activities to use this BaseActivity and reusing the same navigation drawer, or other business logic.

Best practices regarding to data storage

I think it would be good if there were some information regarding to data storing, like:

  • best practices when writing Content Providers
  • when to use Shared Preferences
  • ORMs, do and don't

Reconsiderations in testing section

Thanks for providing this comprehensive guide.
I think that testing sections should go under a heavy reconsideration.
Android testing support library brings some testing frameworks (like espresso) which changes the way we develop our softwares.
Not to mention special Junit runner for android and Instrumented tests.
Also I think Mockito and Hamcrest libraries should have been mentioned too.
Tnx.

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?

Activities and Fragments best practices advice

Can you please explain me the following paragraph in "Activities and Fragments" best practices section:

"Don't abuse Android-level APIs such as heavily relying on Intent for your app's internal workings. You could affect the Android OS or other applications, creating bugs or lag. For instance, it is known that if your app uses Intents for internal communication between your packages, you might incur multi-second lag on user experience if the app was opened just after OS boot."

Thank you in advance

Wiki

Wiki from github should be more appropriate to structure your documentation of best practices.
(btw it's help me alot 👍 )

String obfuscation?

Thank you very much for the valuable information available here. I have one question thought. Do you know of an obfuscator software (preferably open source) that is able to obfuscate strings inside Android's app class files?

I want to install an Android app on local devices and to write inside the code some credentials for Restful endpoint authentication. I want to make sure that the credentials won't be readable by reverse engineering. Any ideas? Thanks.

Update Testing Section

The testing section is out of date.

We should update it and address at least the following topics and tools:

  • Unit Testing using JUnit 4
  • Mocking, using Mockito
  • Assertions using AssertJ
  • Recommend not to use Roboletric for business logic testing
  • RxJava testing using TestScheduler and TestSubscriber
  • Patterns to improve testability
  • UI testings with Espresso
  • Unit testing structure best practices

ID naming conventions

Please include some best practices about naming conventions for android:id of components in XML layouts.

Testing examples in the Project Template

The project template architecture is pretty nice in demonstrating a clean architecture pattern with RxJava.

It would be nice to see more real-world testing examples, as there seems to be a dearth of those. The ones included help ensuring the test framework works, but more flushed out examples would be nice.

Fragment advice is misleading.

I use Activities, Fragments, and custom views, depending on the context. I don't think it's wise to recommend the Fragment approach as a catch-all solution.

Proper screen-to-screen communication. Android's API does not provide a proper way of sending complex data (e.g., some Java Object) from one activity to another activity. With fragments, however, you can use the instance of an activity as a channel of communication between its child fragments.

If you're relying on non-parcelable/serializable POJOs, how do you retain instance state if the user switches to another application? Speaking of that, there's no mention of instance state, parcelable, or serializable in the doc.

Many Android classes expect fragments. The API for tabs on the ActionBar[1] expects the tab contents to be fragments. Also, if using Google Maps API v2, the recommended solution is MapFragment, even though MapView exists.

The ActionBar Tabs API is now deprecated. There's no benefit to use MapFragment over MapView.

Easy to implement swiping transitions between screens. If a UI screen is a fragment in your application, you can use FragmentPagerAdapter to contain a collection of fragments and implement smooth and interactive transitioning between them. For instance, horizontal swiping of screens.

ViewPager has nothing to do with Fragments. It's trivial to implement your own PagerAdapter, the only reason why one is provided in the support lib for fragments is because fragments have such a complex lifecycle.

Also, Fragments don't support some complex transitions. "The Z adjustment only works for window animations. I thought this was documented, but apparently not." This means you can't do things like fade out one fragment and slide in the other from the right, because the outgoing fragment might be rendered above the incoming one.

Intelligent behavior for "back"

The fragment back stack is far from intelligent. You can't remove an item from the back stack unless it's on top, you can't pop the back stack without triggering an animation without hackish workarounds.

关于使用maven依赖引入最新版这点不是很赞同。

在实际的项目开发中遇到过使用 x.x.+在编译的时候引入了最新版本。由于新版的改动,而导致项目一些方法调用返回非预期,或者方法被弃用,新版库换了新的实现等一系列的问题。所以后来统一使用指定版本,了解新版特性后在决定要不要升级类库。

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

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

Validate Links

Hello, I wrote a tool that can validate README links (valid URLs, not duplicate). It can be run when someone submits a pull request.

It is currently being used by many projects including

Examples

If you are interested, connect this repo to https://travis-ci.org/ and add a .travis.yml file to the project.

See https://github.com/dkhamsing/awesome_bot for options, more information
Feel free to leave a comment 😄

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.