Skip to content

Instantly share code, notes, and snippets.

@Mercandj
Last active April 16, 2023 17:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Mercandj/423ca32b5b99c03d5819492f4efd1bdc to your computer and use it in GitHub Desktop.
Save Mercandj/423ca32b5b99c03d5819492f4efd1bdc to your computer and use it in GitHub Desktop.

Android developer: Principles

Description

Please, first, checkout Developer: Principles. These Android developer principles are the "implementation details" of developer principles on the Android world.

The following advices are very opinionated. Before saying "bullshit", please read the why below in the Details section. One of the main ability of developer is to understand the why. (This advice apply to myself of course. Every tool I'm criticising exist because of a why that I consider less important than the alternative. But for sure, you can disagree).

The goal of this document is more to start a reflection than make you think twice of your code.

Keep in mind that these rules are in a context of a long term support of applications / libraries. These are related to my needs as a developer.

5suohe

Here are best practices as an Android developer.

Principles

# Principles
1 Do not use Fragment -> use Activity and Views
2 Do not use GSON -> use JsonObject
3 Do not use reactive programmation -> use classic programmation
4 Do not use Retrofit and OkHttp with asynchronous API -> use synchronous OkHttp or Ktor
5 Do not use coroutine and suspend -> use synchronous methods on a worker thread
6 Do not use lib for Dep. Inj -> use hand made Dep.Inj
7 Do not use MVVM and Google View Model -> use MVP
8 Do not use Jetpack Compose -> use xml
9 Do not use one gradle modle -> use multiple gradle modules with samples
10 Do no use Toolbar and BottomNavigationView -> use your own custom view
11 Do not use Navigation component -> use your own navigation
12 Split resources by feature matching your packages
13 Prefix resource ids and layout by feature name, prefix layout ids by layout name
14 Do the view binding yourself
15 Do not make variable name, class name or package name impact the apk (when possible)
16 Do not write a BaseActivity (composition over inheritance)
17 Do not code Row/Cell inflation and binding in RecyclerView adapter -> do custom view
18 Do not forward Row/Cell listeners via the RecyclerView adapter -> do stand alone Row/Cell with dedicated MVP

Details

1. Do not use fragment

Why?

  • Because it make less easy to understand what you are doing
  • Because it does not bring notion that Activity and View does not support
  • Fragment were first introduced for Honeycomb (3.0) with the goal of make UI for tablet. With fragments, the idea was to propose screen split in two part with, on the left side a recyclerView with menus, and on the right side the content. "Fragment" concept was bringing Activity like lifecycle more close to the view. But fragment is not the only way to split your UI into smallest part. From the ground up of Android, Views are designed like that.

tweet

"Fragments have the Midas touch but for shitty code. The continued peddling of them as anything but a failed abstraction and case study for abhorrent API design is laughable.", JW point of view.

2. Do not use GSON

  • Because it's heavy
  • Because Moshi is better
  • Because the AOSP already bring a json parser

tweet

"Gson are like fragment", JW's joke about point 1. and 2.

3. Do not use reactive programmation

Reactive programmation is a pradigm that impact the whole project. If done correclty, you cannot isolate the "rectivity" to a small part of the code (why? consistency).

So, with that in mind, why do I think "reactive programmation" is not the way to go

  • Because one of the main principle, is to keep the code easy to read, understandable most people (juniors...). On board new developer is key! Reactive programmation is not one isolated feature, that change the whole codebase.
  • Because a lot of libraries and third parties will not use reactive programmation

Keep in mind the first rule of developer: keep it simple, easy to read in order to easily onboard new developpers.

4. Do not use Retrofit and OkHttp with asynchronous API -> use synchronous OkHttp or Ktor

Asynchronous API with callback or listener for example are great for single use. I mean, when you need to do multiple asynchronous task, the trap of asynchronous method is the callback hell. Imagine a simple network call that download a .zip. Imagine you want to unzip the zip just after the download. With synchronous API for the network and the unzip, you will be able to code with less callback:

workerThreadHandler.post {
   val zipContent = try {
      val zipFile = networkManager.downloadZipFile()
      zipManager.unzip(zipFile)
   } catch (e: NeworkException) {
      null
   } catch (e: UnzipException) {
      null
   }
   mainThreadHandler.post {
      // Update the UI / Rest of the app with "zipContent"
   }
}

Perso, I do not use Retrofit abstraction to do myself the header / domain / parsing management.

5. Do not use coroutine and suspend -> use synchronous methods on a worker thread

suspend is part of kotlin language. The problem with the suspend keywork is that it's spread accross your project.

7. Do not use MVVM and Google View Model

Why does design pattern for the "view" exist?

  • a. To split the "view" from the "logic" code
  • b. To split the "platform specific" code from the "logic" code

Why the a.

  • To unit test the "logic" part of the code
  • To have the "logic" part of the code working with "any" UI
  • Because UI is more subject to change, and we do not want to break the logic with small UI changes (Uncle Bob argument)
  • To avoid 3000 line long Activity (was the state once, on one of the project I was working on)

Why the b.

  • To be able to run the logic code on your computer (or on a CI)
  • To be able to produce "similar code" whatever the platform (I mean, code with similar concepts)

So, with that, why do I think the Google way of MVVM is not the way to go

  • Because "ViewModel", everywhere else, is a model crafted specificly to meet the View needs
  • Because one of the main principle, is to keep the code easy to read, understandable most people (juniors, non pure Android dev...). Keep in mind that Android is using jvm code (kotlin and java), so a lot of the code an app is platform agnostic.

8. Do not use Jetpack Compose

Because Jetpack Compose cannot be isolated to the view. UI should not drive your architecture. Avoid strong dependency is key to have robust code.

// WIP

13. Prefix resource ids and layout by feature name, prefix layout ids by layout name

On Android, all the resources are accessible by the whole code (resources are not feature private on the same gradle module). To avoid conflict, prefix your resources. This rules is even more important for resources in shared gradle modules / on libraries.

I remember when I was coding on Unity, libraries that have "app_name" resources ^^. That may force your client to use "replace" in xml, this situation could be avoidwith prefix.

14. Do the view binding yourself

With the rule 13. every id is unique so could be long. View binding done by Android can allow you to have field directly accessible with res id as field name. Avoid that. Resource ids are lowercase with _, fields should use cameCase.

Here how to do that:

// On Activity
private val version: TextView by bind(R.id.settings_activity_version)
private fun <T : View> Activity.bind(@IdRes res: Int): Lazy<T> {
    return lazy(LazyThreadSafetyMode.NONE) { findViewById(res) }
}

// On CustomView
private val view = inflate(context, R.layout.section_bar_view, this)
private val galleryIconOn: ImageView = bind(R.id.section_bar_view_gallery_icon_on)
@Suppress("SameParameterValue")
private fun <T : View> bind(@IdRes id: Int): T {
    @Suppress("RemoveExplicitTypeArguments")
    return view.findViewById<T>(id)
}

15. Do not make variable name, class name or package name having an impact on the binary when possible

  • When you use GSON without annotation for example, the field name of the class are use as JSON keys
  • When you have an enum and use ColorEnum.WHITE.name
  • When you use java reflect

When possible, you should avoid having your code depending on itself (exception for the java reflect, sometimes, no other choice)

Why?

Because be able to refacto without any fear is what's make the codebase robust. You should be able to change variable and classe names without breaking the product. As a developer, it is fair to assume we can rename a classes whithout breaking the product.


Other articles and projects on Mercandj.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment