└── 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 |
--------------------------------------------------------------------------------