└── guides ├── best.md └── techstack.md /guides/best.md: -------------------------------------------------------------------------------- 1 | Android Coding Best Practices 2 | ============================= 3 | 4 | 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. 5 | 6 | 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. 7 | 8 | Code 9 | ---- 10 | 11 | - Follow OOP principles, SOLID principles, DRY and KISS. [#PRNCPL](#PRNCPL) 12 | - Avoid God classes. A class with hundreds of lines is probably doing too much. Each class should have only one responsibility. [#GODCLS](#GODCLS) 13 | - 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](#GODFUN) 14 | - Be consistent in naming classes, methods, properties, resources, etc. [#CONSIS](#CONSIS) 15 | - Use meaningful names for variables. For example, "e" is a bad name. [#MNGFUL](#MNGFUL) 16 | - 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](#POSITN) 17 | - Don't comment out unused code, just delete it. [#UNUSED](#UNUSED) 18 | - 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](#LIBINI) 19 | - Don't show any logs in production build. [#LOGPRD](#LOGPRD) 20 | - Enhance crash reporting with custom logs from your app. Make sure to filter out any sensitive data. [#LOGCRA](#LOGCRA) 21 | - Use factory pattern for creating a new Intent or for creating a new instance of a Fragment. [#FACTRY](#FACTRY) 22 | - Use Parcelable rather than Serializable. [#PARCEL](#PARCEL) 23 | - Use RecyclerView rather than ListView. [#RECYCL](#RECYCL) 24 | - 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](#STABID) 25 | - Don't call `notifyDatasetChanged()` on adapters directly. Use DiffUtil for optimized calculations of adapter data. [#DIFFUT](#DIFFUT) 26 | - Consider using ArrayMap/ArraySet instead of HashMap/HashSet. It's designed to be more memory efficient. [#ARRMAP](#ARRMAP) 27 | - Consider using the Data Binding library from Android Jetpack. [#DATBND](#DATBND) 28 | - Don't use `findViewById()` whenever possible. Use the Data Binding library, Kotlin Android Extensions or auto generated View Binding. [#BNDFND](#BNDFND) 29 | - Don't use complicated expressions in data binding. Views should be as dumb as possible. ViewModel is responsible for display logic. [#BNDEXP](#BNDEXP) 30 | - Leverage the power of custom BindingAdapters. [#BNDADP](#BNDADP) 31 | - Don't use `String.format()` in data binding. Use `@string/example_text(data.foo, data.bar)` instead. [#BNDSTR](#BNDSTR) 32 | 33 | Architecture 34 | ------------ 35 | 36 | - Use a dependency injection framework to make code reusable, easily maintainable and testable. [#DEPINJ](#DEPINJ) 37 | - Use MVVM or MVI architecture. UI logic should be implemented in ViewModel. Activity/Fragment serves as a View. [#MVVMAR](#MVVMAR) 38 | - The View layer should keep neither data nor state. These should be stored in some persistent object, such as ViewModel or Redux Store. [#VIEWDT](#VIEWDT) 39 | - ViewModel should contain only presentation logic. Business logic should be implemented in other classes (e.g. repository pattern). [#VMLOGI](#VMLOGI) 40 | - ViewModel must not access Activity Context. [#VMCNTX](#VMCNTX) 41 | - ViewModel must not access `android.view` nor `android.widget` classes. That is View's responsibility. [#VMWIDG](#VMWIDG) 42 | - 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](#SAVSTA) 43 | - Consider using LiveData for holding the current state of a screen. [#LIVDAT](#LIVDAT) 44 | - 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](#UNIDIR) 45 | - Don't use retained Fragments. [#RETFRG](#RETFRG) 46 | - Don't use Loaders. [#LOADER](#LOADER) 47 | - Don't use long-standing Service if it is not absolutely necessary. Better to use JobScheduler or WorkManager. [#SERVIC](#SERVIC) 48 | - Consider using Android Navigation Component. [#NAVIGA](#NAVIGA) 49 | 50 | Kotlin 51 | ------ 52 | 53 | - Follow [Android Kotlin Style Guide](https://developer.android.com/kotlin/style-guide). [#KTSTYL](#KTSTYL) 54 | - Use Android KTX extensions. [#KTXAND](#KTXAND) 55 | - Consider using Kotlin Coroutines. [#KTCORO](#KTCORO) 56 | - 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](#KTEXPR) 57 | - Don't use non-null assertion `!!` if it's not absolutely necessary. [#KTNULL](#KTNULL) 58 | - Don't use nullable types for non-null variables with delayed initialization. Use `lateinit` for this. [#KTLATE](#KTLATE) 59 | - Use `when` expression instead of long `if-else-if` chain. [#KTWHEN](#KTWHEN) 60 | - Use data classes for entity objects. [#KTDATA](#KTDATA) 61 | - Use Kotlin scope functions from the standard library (`let`, `run`, `also`, `apply`, `with`) to structure the code in a more idiomatic way. [#KTSCOP](#KTSCOP) 62 | - 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](#KTEXTF) 63 | - Consider using inline classes instead of plain primitives (e.g. currency, time). [#KTINLN](#KTINLN) 64 | 65 | Resources 66 | --------- 67 | 68 | - 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](#HCDRES) 69 | - Be careful with an overly deep hierarchy of layouts and views. Leverage the power of ConstraintLayout. [#DEEPLA](#DEEPLA) 70 | - Watch out for overdraws. [#OVRDRW](#OVRDRW) 71 | - Use vector drawables whenever possible. Ask your designer to provide you with SVG files. [#VCTDRW](#VCTDRW) 72 | - Use adaptive launcher icons. [#ADPICO](#ADPICO) 73 | - 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](#IMGRES) 74 | - Distinguish between themes and styles. Put them in separate XML files. [#THMSTY](#THMSTY) 75 | - Use `ThemeOverlay` theme descendants to apply local changes to themes. [#THMOVR](#THMOVR) 76 | - Use `android:textAppearance` for text style. [#TXTAPR](#TXTAPR) 77 | - 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#type-scale) styles from `TextAppearance.AppCompat`. [#TXTSTY](#TXTSTY) 78 | - Use XML attributes in the `tools` namespace (`tools:src`, `tools:listitem`, `tools:visibility`, etc.) that enable useful design-time features. [#TOOLAT](#TOOLAT) 79 | - Use `@tools:sample/*` resources to inject placeholder data or images into views. Avoid using custom sample resources which increase APK size. [#SAMPLE](#SAMPLE) 80 | 81 | Project 82 | ------- 83 | 84 | - Follow [Semantic Versioning](http://semver.org/) for app versioning. [#SEMVER](#SEMVER) 85 | - Don't use dynamic versions for dependencies, such as "1.0.+". [#DYNVER](#DYNVER) 86 | - Target SDK should be set to the maximum API level. [#TGTSDK](#TGTSDK) 87 | - Separate the project into multiple Gradle modules by layer and feature. Modules can be built in parallel or isolated, which reduces build time. [#GRDMOD](#GRDMOD) 88 | - 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](#GRDBRF) 89 | - Consider using `buildSrc` folder with `Versions`, `Dependencies` and custom tasks or plugins for the whole project. [#GRDSRC](#GRDSRC) 90 | - 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](#CONFIG) 91 | - Use a continuous integration service to build your project, test your code and deploy APK file to the Play Store. [#CONINT](#CONINT) 92 | 93 | Git 94 | --- 95 | 96 | - Follow [STRV Git Guidelines](https://github.com/strvcom/strv-guidelines/blob/master/git.md). [#GITGDL](#GITGDL) 97 | - Follow [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/). [#GITMSG](#GITMSG) 98 | - Capitalize the subject line in commit message. [#GITCAP](#GITCAP) 99 | - Use the imperative mood in the subject line of a commit message. [#GITIMP](#GITIMP) 100 | - Consider using Git Flow or a similar branching system. [#GITFLO](#GITFLO) 101 | 102 | UI & UX 103 | ------- 104 | 105 | - Follow [Material Design Guidelines](https://material.io/design/). [#MATERL](#MATERL) 106 | - The app should support both landscape and portrait mode, even in a situation where the portrait mode is forced in production build. [#LANDSC](#LANDSC) 107 | - The app should support split screen mode. [#SPLTSC](#SPLTSC) 108 | - Handle all possible edge cases and states: content, progress, offline, empty, etc. [#STATES](#STATES) 109 | - Show progress indicator when something is loading. [#PRGIND](#PRGIND) 110 | - Don't use progress dialogs. [#PRGDLG](#PRGDLG) 111 | - Clickable views should have a ripple touch feedback. [#RIPPLE](#RIPPLE) 112 | - Clickable views should have at least `48dp` width/height. [#CLCSIZ](#CLCSIZ) 113 | - Dialogs should not disappear after an orientation change or Activity restore. [#ORIDLG](#ORIDLG) 114 | - ScrollView or RecyclerView should keep the scroll position after an orientation change, Activity restore or popping the backstack. [#ORISCR](#ORISCR) 115 | - Selectable views should keep the select state after an orientation change or Activity restore. [#ORISEL](#ORISEL) 116 | - If it's already loaded, don't load data again after an orientation change or Activity restore. [#ORILOA](#ORILOA) 117 | - Strive to make the app offline first by using a caching method (e.g. Room, Firestore, Realm). [#OFFLIN](#OFFLIN) 118 | - Always test the app on various screen sizes, densities and Android versions. [#TSTAPP](#TSTAPP) 119 | - 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](https://gist.github.com/mlykotom/18d48ec6cc75f935ed7658867cedad5f). [#TSTKIL](#TSTKIL) 120 | -------------------------------------------------------------------------------- /guides/techstack.md: -------------------------------------------------------------------------------- 1 | Tech Stack 2 | ========== 3 | 4 | List of the most common technologies that we use in Android apps. 5 | 6 | Languages 7 | --------- 8 | 9 | - [Kotlin](https://kotlinlang.org) 10 | - [Java](https://www.java.com) 11 | 12 | Libraries 13 | --------- 14 | 15 | - [Android Jetpack](https://developer.android.com/jetpack/) 16 | - [Apollo Kotlin](https://github.com/apollographql/apollo-kotlin) 17 | - [Coil](https://github.com/coil-kt/coil) 18 | - [Dagger](https://github.com/google/dagger) 19 | - [ExoPlayer](https://github.com/google/ExoPlayer) 20 | - [Firebase](https://firebase.google.com) 21 | - [Hilt](https://developer.android.com/training/dependency-injection/hilt-android) 22 | - [Jetpack Compose](https://developer.android.com/jetpack/compose) 23 | - [Koin](https://github.com/InsertKoinIO/koin) 24 | - [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) 25 | - [LeakCanary](https://github.com/square/leakcanary) 26 | - [Moshi](https://github.com/square/moshi) 27 | - [MVVM](https://developer.android.com/topic/architecture) 28 | - [OkHttp](https://github.com/square/okhttp) 29 | - [Retrofit](https://github.com/square/retrofit) 30 | - [Room](https://developer.android.com/training/data-storage/room) 31 | - [SQLDelight](https://github.com/cashapp/sqldelight) 32 | - [Timber](https://github.com/JakeWharton/timber) 33 | 34 | Testing 35 | ------- 36 | 37 | - [Jetpack Compose](https://developer.android.com/jetpack/compose/testing) 38 | - [JUnit](https://github.com/junit-team/junit4) 39 | - [Mockito](https://github.com/mockito/mockito) 40 | 41 | Tools 42 | ----- 43 | 44 | - [ADB Idea](https://plugins.jetbrains.com/plugin/7380-adb-idea) 45 | - [Charles](https://www.charlesproxy.com) 46 | - [Detekt](https://github.com/detekt/detekt) 47 | - [Ktlint](https://github.com/pinterest/ktlint) 48 | - [Postman](https://www.getpostman.com) 49 | - [Scrcpy](https://github.com/Genymobile/scrcpy) 50 | - [Stetho](http://facebook.github.io/stetho/) 51 | - [Vysor](http://vysor.io) 52 | 53 | CI 54 | -- 55 | 56 | - [GitHub Actions](https://github.com/features/actions) 57 | 58 | Design 59 | ------ 60 | 61 | - [Material Design](https://material.io) 62 | - [Material Icons](https://material.io/resources/icons/) 63 | --------------------------------------------------------------------------------