Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
1 contributor

Users who have contributed to this file

119 lines (103 sloc) 12.2 KB

Android Coding Best Practices

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

Strive to follow these rules when working on STRV projects. Each rule has a special # 6-chars identifier, allowing us to easily refer to it.

Code

  • Follow OOP principles, SOLID principles, DRY and KISS. #PRNCPL
  • Avoid God classes. A class with hundreds of lines is probably doing too much. Each class should have only one responsibility. #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
  • Be consistent in naming classes, methods, properties, resources, etc. #CONSIS
  • Use meaningful names for variables. For example, "e" is a bad name. #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
  • Don't comment out unused code, just delete it. #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
  • Don't show any logs in production build. #LOGPRD
  • Enhance crash reporting with custom logs from your app. Make sure to filter out any sensitive data. #LOGCRA
  • Use factory pattern for creating a new Intent or for creating a new instance of a Fragment. #FACTRY
  • Use Parcelable rather than Serializable. #PARCEL
  • Use RecyclerView rather than ListView. #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
  • Don't call notifyDatasetChanged() on adapters directly. Use DiffUtil for optimized calculations of adapter data. #DIFFUT
  • Consider using ArrayMap/ArraySet instead of HashMap/HashSet. It's designed to be more memory efficient. #ARRMAP
  • Consider using the Data Binding library from Android Jetpack. #DATBND
  • Don't use findViewById() whenever possible. Use the Data Binding library, Kotlin Android Extensions or auto generated View Binding. #BNDFND
  • Don't use complicated expressions in data binding. Views should be as dumb as possible. ViewModel is responsible for display logic. #BNDEXP
  • Leverage the power of custom BindingAdapters. #BNDADP
  • Don't use String.format() in data binding. Use @string/example_text(data.foo, data.bar) instead. #BNDSTR

Architecture

  • Use a dependency injection framework to make code reusable, easily maintainable and testable. #DEPINJ
  • Use MVVM or MVI architecture. UI logic should be implemented in ViewModel. Activity/Fragment serves as a View. #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
  • ViewModel should contain only presentation logic. Business logic should be implemented in other classes (e.g. repository pattern). #VMLOGI
  • ViewModel must not access Activity Context. #VMCNTX
  • ViewModel must not access android.view nor android.widget classes. That is View's responsibility. #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
  • Consider using LiveData for holding the current state of a screen. #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
  • Don't use retained Fragments. #RETFRG
  • Don't use Loaders. #LOADER
  • Don't use long-standing Service if it is not absolutely necessary. Better to use JobScheduler or WorkManager. #SERVIC
  • Consider using Android Navigation Component. #NAVIGA

Kotlin

  • Follow Android Kotlin Style Guide. #KTSTYL
  • Use Android KTX extensions. #KTXAND
  • Consider using Kotlin Coroutines. #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
  • Don't use non-null assertion !! if it's not absolutely necessary. #KTNULL
  • Don't use nullable types for non-null variables with delayed initialization. Use lateinit for this. #KTLATE
  • Use when expression instead of long if-else-if chain. #KTWHEN
  • Use data classes for entity objects. #KTDATA
  • Use Kotlin scope functions from the standard library (let, run, also, apply, with) to structure the code in a more idiomatic way. #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
  • Consider using inline classes instead of plain primitives (e.g. currency, time). #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
  • Be careful with an overly deep hierarchy of layouts and views. Leverage the power of ConstraintLayout. #DEEPLA
  • Watch out for overdraws. #OVRDRW
  • Use vector drawables whenever possible. Ask your designer to provide you with SVG files. #VCTDRW
  • Use adaptive launcher icons. #ADPICO
  • Use PNG only for graphics (not vectors) and JPEG for photos to optimize APK size. Consider using WebP image format and an image compression tool. #IMGRES
  • Distinguish between themes and styles. Put them in separate XML files. #THMSTY
  • Use ThemeOverlay theme descendants to apply local changes to themes. #THMOVR
  • Use android:textAppearance for text style. #TXTAPR
  • Use predefined text sizes and try to avoid using custom values. Use directly or extend Material text appearance styles from TextAppearance.AppCompat. #TXTSTY
  • Use XML attributes in the tools namespace (tools:src, tools:listitem, tools:visibility, etc.) that enable useful design-time features. #TOOLAT
  • Use @tools:sample/* resources to inject placeholder data or images into views. Avoid using custom sample resources which increase APK size. #SAMPLE

Project

  • Follow Semantic Versioning for app versioning. #SEMVER
  • Don't use dynamic versions for dependencies, such as "1.0.+". #DYNVER
  • Target SDK should be set to the maximum API level. #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
  • 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
  • Consider using buildSrc folder with Versions, Dependencies and custom tasks or plugins for the whole project. #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, config class, etc. #CONFIG
  • Use a continuous integration service to build your project, test your code and deploy APK file to the Play Store. #CONINT

Git

UI & UX

  • Follow Material Design Guidelines. #MATERL
  • The app should support both landscape and portrait mode, even in a situation where the portrait mode is forced in production build. #LANDSC
  • The app should support split screen mode. #SPLTSC
  • Handle all possible edge cases and states: content, progress, offline, empty, etc. #STATES
  • Show progress indicator when something is loading. #PRGIND
  • Don't use progress dialogs. #PRGDLG
  • Clickable views should have a ripple touch feedback. #RIPPLE
  • Clickable views should have at least 48dp width/height. #CLCSIZ
  • Dialogs should not disappear after an orientation change or Activity restore. #ORIDLG
  • ScrollView or RecyclerView should keep the scroll position after an orientation change, Activity restore or popping the backstack. #ORISCR
  • Selectable views should keep the select state after an orientation change or Activity restore. #ORISEL
  • If it's already loaded, don't load data again after an orientation change or Activity restore. #ORILOA
  • Strive to make the app offline first by using a caching method (e.g. Room, Firestore, Realm). #OFFLIN
  • Always test the app on various screen sizes, densities and Android versions. #TSTAPP
  • Always test if the app works properly when the system kills it on inactivity. You can use ADB Idea plugin, "Don't keep activities" in developer options or ADB script. #TSTKIL
You can’t perform that action at this time.