# Android Coding Best Practices for Developers

[Petr Nohejl](https://www.strv.com/blog/authors/petr) Android Engineering Director

[Tomas Mlynaric](https://www.strv.com/blog/authors/tomas) Android Engineer

---

Strict rules or chaos? The answer lies in finding the middle ground. And that's exactly what we did with STRV’s [best.md](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io) — a set of 'nice to follow' guidelines useful when working in larger teams. Helps to keep the code nice, clean and uniform. It also gives new people joining the project a flat learning curve; once they’re familiar with the principles, it’s much easier to get acquainted with the codebase.

Each rule has a special #6-chars identifier, allowing us to easily refer to it. We’ve worked hard on this list of dos & don’ts, and we’re happy to share it with you.

## CODE
- Follow OOP principles, SOLID principles, DRY and KISS. [#PRNCPL](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#PRNCPL)
- Avoid God classes. A class with hundreds of lines is probably doing too much. Each class should have only one responsibility. [#GODCLS](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#GODCLS)
- Avoid complicated functions. Every function should be doing only one thing. If possible, don't mix side effects with computation logic inside one function. [#GODFUN](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#GODFUN)
- Be consistent in naming classes, methods, properties, resources, etc. [#CONSIS](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#CONSIS)
- Use meaningful names for variables. For example, "e" is a bad name. [#MNGFUL](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#MNGFUL)
- Don't add new classes, methods, properties, resources, etc., automatically at the end of a file. Consider the proper position in the file where the element should be added. Order lifecycle methods chronologically. [#POSITN](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#POSITN)
- Don't comment out unused code, just delete it. [#UNUSED](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#UNUSED)
- Be careful with initializing multiple libraries in `Application.onCreate()`. Do this stuff off the main thread whenever possible to speed up the start of the app. [#LIBINI](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#LIBINI)
- Don't show any logs in production build. [#LOGPRD](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#LOGPRD)
- Enhance crash reporting with custom logs from your app. Make sure to filter out any sensitive data. [#LOGCRA](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#LOGCRA)
- Use factory pattern for creating a new Intent or for creating a new instance of a Fragment. [#FACTRY](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#FACTRY)
- Use Parcelable rather than Serializable. [#PARCEL](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#PARCEL)
- Use RecyclerView rather than ListView. [#RECYCL](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#RECYCL)
- Use stable ids in RecyclerView adapter whenever possible. You will achieve better performance because ViewHolders can be reused after `notifyDataSetChanged()` and you will get animations. [#STABID](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#STABID)
- Don't call `notifyDatasetChanged()` on adapters directly. Use DiffUtil for optimized calculations of adapter data. [#DIFFUT](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#DIFFUT)
- Consider using ArrayMap/ArraySet instead of HashMap/HashSet. It's designed to be more memory efficient. [#ARRMAP](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#ARRMAP)
- Consider using the Data Binding library from Android Jetpack. [#DATBND](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#DATBND)
- Don't use `findViewById()` whenever possible. Use the Data Binding library, Kotlin Android Extensions or auto generated View Binding. [#BNDFND](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#BNDFND)
- Don't use complicated expressions in data binding. Views should be as dumb as possible. ViewModel is responsible for display logic. [#BNDEXP](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#BNDEXP)
- Leverage the power of custom BindingAdapters. [#BNDADP](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#BNDADP)
- Don't use `String.format()` in data binding. Use `@string/example_text(data.foo, data.bar)` instead. [#BNDSTR](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#BNDSTR)

## ARCHITECTURE
- Use a dependency injection framework to make code reusable, easily maintainable and testable. [#DEPINJ](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#DEPINJ)
- Use MVVM or MVI architecture. UI logic should be implemented in ViewModel. Activity/Fragment serves as a View. [#MVVMAR](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#MVVMAR)
- The View layer should keep neither data nor state. These should be stored in some persistent object, such as ViewModel or Redux Store. [#VIEWDT](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#VIEWDT)
- ViewModel should contain only presentation logic. Business logic should be implemented in other classes (e.g., repository pattern). [#VMLOGI](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#VMLOGI)
- ViewModel must not access Activity Context. [#VMCNTX](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#VMCNTX)
- ViewModel must not access `android.view` nor `android.widget` classes. That is View's responsibility. [#VMWIDG](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#VMWIDG)
- Always check whether the app handles saving the persistent state. When the system kills the app, ViewModel doesn’t save the state. Use plain `onSaveInstanceState()` or Saved State module for ViewModel. [#SAVSTA](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#SAVSTA)
- Consider using LiveData for holding the current state of a screen. [#LIVDAT](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#LIVDAT)
- Strive to have a unidirectional flow of data with observer patterns. Don't imperatively set stuff if observing is possible. Leverage MediatorLiveData or similar transformations. [#UNIDIR](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#UNIDIR)
- Don't use retained Fragments. [#RETFRG](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#RETFRG)
- Don't use Loaders. [#LOADER](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#LOADER)
- Don't use long-standing Service if it is not absolutely necessary. Better to use JobScheduler or WorkManager. [#SERVIC](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#SERVIC)
- Consider using Android Navigation Component. [#NAVIGA](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#NAVIGA)

## KOTLIN
- Follow [Android Kotlin Style Guide](https://developer.android.com/kotlin/style-guide?ref=strv.ghost.io). [#KTSTYL](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#KTSTYL)
- Use Android KTX extensions. [#KTXAND](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#KTXAND)
- Consider using Kotlin Coroutines. [#KTCORO](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#KTCORO)
- Don't write unreadable monster expressions. Obsessing over getting by with a single expression and over utilizing smart casts can lead to pretty unreadable code. [#KTEXPR](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#KTEXPR)
- Don't use non-null assertion `!!` if it's not absolutely necessary. [#KTNULL](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#KTNULL)
- Don't use nullable types for non-null variables with delayed initialization. Use `lateinit` for this. [#KTLATE](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#KTLATE)
- Use `when` expression instead of long `if-else-if` chain. [#KTWHEN](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#KTWHEN)
- Use data classes for entity objects. [#KTDATA](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#KTDATA)
- Use Kotlin scope functions from the standard library (`let`, `run`, `also`, `apply`, `with`) to structure the code in a more idiomatic way. [#KTSCOP](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#KTSCOP)
- Extension functions are not a replacement for all utility functions. Extension functions are good for extending existing abstractions. Don't abuse them if an extension semantically doesn't make sense. For example, `Int.px()` is a semantically wrong extension. [#KTEXTF](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#KTEXTF)
- Consider using inline classes instead of plain primitives (e.g., currency, time). [#KTINLN](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#KTINLN)

## RESOURCES
- Don't hardcode resource values with the exception of 8dp grid dimen values (`8dp`, `16dp`, `24dp`, `32dp`, `48dp`, etc.). Put resource values in separate XML files. [#HCDRES](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#HCDRES)
- Be careful with an overly deep hierarchy of layouts and views. Leverage the power of ConstraintLayout. [#DEEPLA](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#DEEPLA)
- Watch out for overdraws. [#OVRDRW](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#OVRDRW)
- Use vector drawables whenever possible. Ask your designer to provide you with SVG files. [#VCTDRW](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#VCTDRW)
- Use adaptive launcher icons. [#ADPICO](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#ADPICO)
- Use PNG only for graphics (not vectors) and JPEG for photos to optimize APK size. Consider using an image compression tool. [#IMGRES](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#IMGRES)
- Distinguish between themes and styles. Put them in separate XML files. [#THMSTY](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#THMSTY)
- Use `ThemeOverlay` theme descendants to apply local changes to themes. [#THMOVR](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#THMOVR)
- Use `android:textAppearance` for text style. [#TXTAPR](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#TXTAPR)
- Use predefined text sizes and try to avoid using custom values. Use directly or extend [Material text appearance](https://material.io/design/typography/the-type-system.html?ref=strv.ghost.io#type-scale) styles from `TextAppearance.AppCompat`. [#TXTSTY](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#TXTSTY)
- Use XML attributes in the `tools` namespace (`tools:src`, `tools:listitem`, `tools:visibility`, etc.) that enable useful design-time features. [#TOOLAT](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#TOOLAT)
- Use `@tools:sample/*` resources to inject placeholder data or images into views. Avoid using custom sample resources which increase APK size. [#SAMPLE](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#SAMPLE)

## PROJECT
- Follow [Semantic Versioning](http://semver.org/?ref=strv.ghost.io) for app versioning. [#SEMVER](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#SEMVER)
- Don't use dynamic versions for dependencies, such as "1.0.+". [#DYNVER](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#DYNVER)
- Target SDK should be set to the maximum API level. [#TGTSDK](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#TGTSDK)
- Separate the project into multiple Gradle modules by layer and feature. Modules can be built in parallel or isolated, which reduces build time. [#GRDMOD](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#GRDMOD)
- Keep the `build.gradle` file brief and don't overfill it with config that is not relevant to this specific module or app. You can extract some common tasks or settings to a separate file. [#GRDBRF](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#GRDBRF)
- Consider using `buildSrc` folder with `Versions`, `Dependencies` and custom tasks or plugins for the whole project. [#GRDSRC](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#GRDSRC)
- Use a config class to store all configuration values (API URLs, API keys, log settings, etc.) in one place. Avoid defining config values in multiple places like `build.gradle`, `AndroidManifest.xml`, or config class. [#CONFIG](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#CONFIG)
- Use a continuous integration service to build your project, test your code and deploy APK file to the Play Store. [#CONINT](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#CONINT)

## GIT
- Follow [STRV Git Guidelines](https://github.com/strvcom/strv-guidelines/blob/master/git.md?ref=strv.ghost.io). [#GITGDL](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#GITGDL)
- Follow [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/?ref=strv.ghost.io). [#GITMSG](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#GITMSG)
- Capitalize the subject line in commit message. [#GITCAP](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#GITCAP)
- Use the imperative mood in the subject line of a commit message. [#GITIMP](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#GITIMP)
- Consider using Git Flow or a similar branching system. [#GITFLO](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#GITFLO)

## UI & UX
- Follow [Material Design Guidelines](https://material.io/design/?ref=strv.ghost.io). [#MATERL](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#MATERL)
- The app should support both landscape and portrait mode, even in a situation where the portrait mode is forced in production build. [#LANDSC](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#LANDSC)
- The app should support split screen mode. [#SPLTSC](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#SPLTSC)
- Handle all possible edge cases and states: content, progress, offline, empty, etc. [#STATES](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#STATES)
- Show progress indicator when something is loading. [#PRGIND](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#PRGIND)
- Don't use progress dialogs. [#PRGDLG](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#PRGDLG)
- Clickable views should have a ripple touch feedback. [#RIPPLE](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#RIPPLE)
- Clickable views should have at least 48dp width/height. [#CLCSIZ](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#CLCSIZ)
- Dialogs should not disappear after an orientation change or Activity restore. [#ORIDLG](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#ORIDLG)
- ScrollView or RecyclerView should keep the scroll position after an orientation change, Activity restore or popping the backstack. [#ORISCR](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#ORISCR)
- Selectable views should keep the select state after an orientation change or Activity restore. [#ORISEL](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#ORISEL)
- If it’s already loaded, don't load data again after an orientation change or Activity restore. [#ORILOA](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#ORILOA)
- Strive to make the app offline first by using a caching method (e.g., Room, Firestore, Realm). [#OFFLIN](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#OFFLIN)
- Always test the app on various screen sizes, densities, and Android versions. [#TSTAPP](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#TSTAPP)
- Always test if the app works properly when the system kills it on inactivity. Use ADB Idea plugin, "Don't keep activities" in developer options or [ADB script](https://gist.github.com/mlykotom/18d48ec6cc75f935ed7658867cedad5f?ref=strv.ghost.io). [#TSTKIL](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io#TSTKIL)

That’s all from us. You can also find [Android Coding Best Practices on our GitHub](https://github.com/strvcom/android/blob/master/guides/best.md?ref=strv.ghost.io). We’d like to thank our colleague, **Jakub Kinst**, for his contribution to this article. And thank **you** for reading all the way to the end. Please let us know if you have any questions!

[REACH OUT](https://www.strv.com/contact?ref=strv.ghost.io)

---

Don't miss anything