Wrapping Mockito Mocks for Reusability

My general advice about Mockito is to try and avoid it when you can. Ryan Harter has an awesome companion post called Replacing Mocks which shows some potential pitfalls of using Mockito Mocks, as well as how to avoid using it by restructuring code. However, sometimes Mockito can be the right tool to use for mocking dependencies in a unit test when a code refactor isn’t possible due to constraints. I use Mockito-Kotlin which helps leverage Mockito in Kotlin code.

In this post I show how you can wrap a Mockito mock to avoid using verbose syntax (“whenever”, “verify”, etc.) all over the place. I call this wrapper around the Mockito mock a “Fake”.

// Fake Wrapper around Mockito Mock (See implementation below)
val fakeOven = FakeOven()

// Access and Use Mockito Mock
val oven : Oven = fakeOven.mock

// Clean API to Setup Mocks (using Wrapper)
fakeOven.givenOvenResult(OvenResult.Success)

// Clean API to Verify Mocks (using Wrapper)
fakeOven.thenOvenSetTo(temperatureFahrenheit = 350, timeMinutes = 30)

Benefits:

  • Single Responsibility – Mocking logic out of your test class.
  • Cleaner Tests – Avoid using “when”, “whenever” and “verify” all over the place.
  • Less Duplication – Can be reused across tests and for future tests you may write.

Example: Baking with an Oven

In this example we bake a Cake which requires an Oven. We need to mock the Oven.

NOTE: We could try various approaches for this example, but for the purpose of explaining this strategy, we’ll use the “Fake” Mockito wrapper.

/** Class that uses [Oven] */
class Dessert(val oven: Oven) {
    fun bakeCake(): OvenResult {
        oven.setTemperatureFahrenheit(350)
        oven.setTimeMinutes(30)
        return oven.start()
    }
}
/** Class we will use Mockito to Mock */
class Oven {
    fun setTemperatureFahrenheit(tempF: Int) {
        // ...
    }

    fun setTimeMinutes(minutes: Int) {
        // ...
    }

    fun start(): OvenResult {
        // ...
    }
}
/** Whether the Oven command was successful, or something happened */
sealed class OvenResult {
    object Success : OvenResult()
    data class Failure(val e: Exception) : OvenResult()
}

Original Test 🤷🏽‍♂️

import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import org.junit.Test

/** Test Dessert Baking */
class DessertTest {

    @Test
    fun bakeCakeSuccess() {
        val oven: Oven = mock()
        val dessert = Dessert(oven)

        // Setup
        whenever(oven.start()).thenReturn(OvenResult.Success)

        // Execute Code
        dessert.bakeCake()

        // Verification
        verify(oven).setTemperatureFahrenheit(350)
        verify(oven).setTimeMinutes(30)
    }
}

FakeOven – Mockito Mock Wrapper 🤔

/** Wraps the Mockito mock for reuse */
class FakeOven {

    val mock: Oven = mock()

    fun givenOvenResult(ovenResult: OvenResult) {
        // Setup
        whenever(mock.start()).thenReturn(ovenResult)
    }

    fun thenOvenSetTo(temperatureFahrenheit: Int, timeMinutes: Int) {
        // Verification
        verify(mock).setTemperatureFahrenheit(temperatureFahrenheit)
        verify(mock).setTimeMinutes(timeMinutes)
    }
}

Updated Test – Using Fake Mockito Wrapper ✅

class DessertTestWithFake {

    @Test
    fun bakeCakeSuccess() {
        val fakeOven = FakeOven()
        val dessert = Dessert(fakeOven.mock)

        fakeOven.givenOvenResult(OvenResult.Success)
        dessert.bakeCake()
        fakeOven.thenOvenSetTo(
            temperatureFahrenheit = 350,
            timeMinutes = 30
        )
    }
}

We use “fakeOven.mock” to fulfill the “Oven” dependency, and control the behavior using the wrapper we have created.

val fakeOven = FakeOven()
val oven : Oven = fakeOven.mock

Conclusion

Isolate usage of Mockito, and avoid scattering it all over your tests. By using this pattern of wrapping a Mockito Mock in a fake, your tests that require Mockito are a little bit better.

Unlocking Biometric Prompt – Fingerprint & Face Unlock

AndroidX Biometric gives us a single API for supporting Biometrics on Android devices via the BiometricPrompt, and a fallback Fingerprint dialog for API 23-27.  This post does a side-by-side comparison of what it looks like on the Pixel 4, Pixel 3, and an API 26 Emulator to show you what it looks like on different devices, hardware and different builder configurations.

Currently, the Pixel 4 is the only device that supports Face Unlock via the Biometric Prompt. There aren’t even emulators that support it.  I ended up with a Pixel 4, and wanted to create this post with you to save you some 💰 and⌚.

What is the Android Biometric Prompt?

The Android Biometric Prompt was released as part of the Android OS in API 28 (Pie) to replace the FingerprintManager.  Its goal is to make a standard way of interacting with Biometrics via the operating system, and also support multiple types of biometric types such as Fingerprint, Face & TBD.

One of the downsides of the Biometric Prompt is that we are asked to use the terminology “Biometric” instead of “Fingerprint” or “Face” because the BiometricManager doesn’t tell us the type of biometric the user will use.  Read more about how to provide better user experiences through tailored biometric messaging in my previous post.

Using AndroidX Biometric

I’m going to show you various configurations of the library in this post, but refer the documentation from Google for more information.

BiometricPrompt.PromptInfo.Builder()
 .setTitle("Authenticate with Face")
 .setNegativeButtonText("Cancel") 
 .setConfirmationRequired(true)
 .build()
Face Unlock Success

Face Unlock Fail & Retry

Confirmation Required

There is a configuration parameter for Biometric Prompt Info Builder called “setConfirmationRequired” which, when set to false, passively authenticates the user without any interaction (other than looking at the phone). Note: This only works for Face Unlock since a Fingerprint is confirmation.

.setConfirmationRequired(false)

.setConfirmationRequired(true)

NOTE: There is a setting which allows a user to ALWAYS confirm when using Face Unlock, so be aware of that when building your applications.

Conclusion

Use AndroidX Biometric.  It feels like they were still working out the kinks when they release Biometric Prompt in API 28.  The AndroidX Library has some device specific workarounds and the fallback dialog for API 23-27.  It’s weird that there isn’t better documentation about the user experience for each biometric type, which is why I wrote this post. Hopefully you won’t have to spend hundreds of dollars on a Pixel 4 now, and have a better idea of what the Biometric Prompt looks like under different configurations.

Links:

Companion Video on AsyncAndroid

Android Device Mirroring and Recording

Mirroring and Recording what is on your physical Android Device to your computer isn’t trivial, but is an important skill to have as a developer. Being able to share and record what you see on your Android screen is super helpful for live demos, your GitHub PRs, and blog posts. There are a lot of ways to do this, but there is no perfect way of doing it.  I’ll walk you through my flow in this post.

I used Vysor for a long time, and think it’s a great tool to use to mirror your device onto your computer.  Vysor is really easy to use, but the free version does have feature limitations such as mirroring quality, and advertisements every 15 minutes. When I tried scrcpy with my development setup, I found that for me it worked the best out of any of the other tools to do this.

See the scrcpy GitHub page for all the instructions, but if you are on a Mac, already have “adb” configured, and have homebrew, then just run brew install scrcpy.  After it’s installed you can just type “scrcpy” into your terminal and it’ll launch device mirroring.

Now that you have your Android device mirroring on your computer, you need to record it.  There are a lot of tools that allow you to screen record into a GIF, but I use Kap.  You can launch Kap via the menu bar with this icon:

Then select an area of your screen manually,

… or choose an open application window.

After you are done recording, stop the recording with the button in the menu bar.

Trim your content, select resolution, frames per second (FPS), export format (GIF, MP4), and export!

Drag and drop the file into your GitHub PR.  NOTE: 10MB is the limit for image attachments in a PR, so adjust your frames per second and image resolution to find the right size.

Your PR has a GIF of your device! 🎉 This makes your PR a lot easier to understand to a reviewer, and anyone looking back at this PR in the future.

Conclusion

Adding GIFs to PRs and blog posts really keep readers engaged.  It especially did if you are actually reading this far 😎.  I hope my you find my recommendations something that help your Android development and content creation!

Links:

Companion Video on AsyncAndroid

Where Should I Put Kotlin Code in an Android Project?

Based on the results of a poll of 371 Android developers, the majority of responses endorse using “src/main/java” for Kotlin code in Android only projects.  My answer is always it depends, but let’s see why you would use “src/main/java” the majority of the time, and why you’d want to use “src/main/kotlin” in some cases.

Why src/main/java?

79% of people say it should go in “src/main/java”.  Here’s why:

  • The official documentation Android Developer Documentation recommends it, and shows additional configuration you must add to support a “src/main/kotlin” source set.
    android {
       sourceSets {
           main.java.srcDirs += 'src/main/kotlin'
       }
    }
    
  • When you create a new project Kotlin project in Android Studio, it uses “src/main/java” as the location for your Kotlin code.
  • I personally really like it for the reason that all of your source is in a single place, making it easier to find related code in a project mixed with Java.  This can help maintain consistency to have all your code in one location for any project that has or had Java code in the past.
  • There are no longer issues with the Kotlin Gradle Plugin which prevented you from mixing Kotlin and Java code in the same directory.

Why src/main/kotlin?

21% of people say it should go in  “src/main/kotlin”.  Here’s why:

  • Some projects like the clean separation of the languages into different directories.  While this is nice idea, from a pragmatic standpoint, having all your code in a single place is the ideal for discovery and browsing in my opinion.
  • Some projects are 100% Kotlin, so having this directory structure clearly calls this is a single to only write code in Kotlin.  This comes with the configuration overhead of each module, but if you share Gradle config between multiple projects, you’d only have to do this once.
  • OkHttp does it, but realize this is because it’s a Kotlin only project that is meant to be used on multiple platforms (including the JVM & Android).

Kotlin Source Sets beyond Android

The answers above only apply for Android only projects.  Kotlin is more than just a programming language used on Android.  It’s being used for Kotlin Multiplatform to create code that runs on iOS, it’s used in Kotlin Native to get blazing fast, low level speed and Kotlin JS to run in your Node server or web application.

You can see how some Kotlin Multiplatform projects are using source source sets such as “commonMain” where common code used for all platforms is kept, and then you can have additional source sets like “iosMain” for platform specific implementations.

I’m not going to pretend that I’m a Kotlin Multiplatform expert, but check out the official documentation, and the KaMPKit Github Repo from TouchLab to see a sample project of Kotlin Multiplatform.

Android Biometrics UX Guide – User Messaging

Users Say: “Biometric…🤷‍♂️🤷‍♀️?”

When I’ve demoed “Biometric” UIs to non-developers, many say:

Why don’t you just say “Fingerprint” or “Face Unlock”?

The reason is that the Biometric APIs have no way to find the type of biometric that will be used.  That’s why we are stuck with using “Biometric” as a catch all.  You can see the terminology being used in Google’s Android Developer Training on Biometric Auth.

We’re also working with the UX / design team on clear iconography/messaging – in the meantime, our suggestion to developers has been to use something along the lines of “Biometric settings”, or “Use biometric”, etc.
– Googler’s Response on Issue Tracker

I have read “BiometricManager” and “BiometricPrompt” enough times to get used to it, but users haven’t.  So let’s see what we can do to create better user messaging.

Option A: Describe “What” Instead of “How”

Say what the user is going to do like “Unlock” or “Login” or “Confirm” or whatever.  Just don’t mention how the user will do it via a biometric.  The system will show the UX for the biometric type anyways in the Biometric Prompt, but your customized wording will be whatever you provide.

 

Consider these scenarios as well:

  • What will you call this on your settings page?
  • What iconography will you use for “Biometric” on a Pixel 4 with Face Unlock? A Fingerprint?  That’s not ideal.
  • How will you encourage your users to use biometrics in your app?  Maybe you could say “Unlock Faster Next Time” and it can be implied that a biometric will be used?

You might be able to get away with this messaging, and if you can, congrats! 🎉

Option B: Write Code and Cross Your Fingers🤞

It’s possible to figure out what biometric will be used the majority of the time, and I’ll show you how. 😎

Running on Pixel 3 Running on Pixel 4

Step 1) See If Device Has Biometric Support

Ask the BiometricManager if it canAuthenticate(), and if it’s successful, or says the user has not enrolled their biometrics, then you know the device is capable.

val biometricManager = BiometricManager.from(context)
val isCapable = when (biometricManager.canAuthenticate()) {
    BiometricManager.BIOMETRIC_SUCCESS,
    BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> true
    else -> false
}

The result of this just tells us that the device is capable of using the BiometricPrompt.

Step 2) Ask PackageManager For Available Features

There are currently only 3 types of Biometrics as of API 29 (Android 10).  The Android PackageManager can be queried to see if these features are available on the device.

// Get Package Manager
val packageManager : PackageManager = context.packageManager

Based on these, you should know, but there are edge cases:

  • One is available – If only one is available and the rest are not, you should feel pretty confident that you know the exact type of biometric that will be used.
  • More than one available – It is possible that a device could have more than one biometric feature.  It could have Face Unlock and Fingerprint.  Android is an open platform, and Google has said that OEMs could do this if they choose.
  • None are available –  If this is the case, and the BiometricManager said you canAuthenticate(), then some sort of biometric is available that we have never seen before.  This could be the case if this code is run on a future version of Android with a Biometric type we don’t know about.

Step 3) Determine Supported Biometrics

Based on all the logic above, we will end up with one of the following results.

sealed class Biometrics {
    object None : Biometrics()
    sealed class Available : Biometrics() {
        object Face : Available()
        object Fingerprint : Available()
        object Iris : Available()
        object Multiple : Available()
        object Unknown : Available()
    }
}

You can then use a “when” statement to create user messaging for a specific biometric hardware type. 🎉

What are future Biometric Types?

We don’t know yet.

Biometric APIs are meant to be more future-looking. We “could” expose something like authenticate(type, info, crypto) etc, but it exposes more API surface and thus has the chance of causing more fragmentation across OEMs.
– Googler’s Response on Issue Tracker

In order to be more open ended, these Biometric APIs are built in a way where generic messaging is the recommended approach currently.

Conflicts with Material Design Guidelines

The Material Design guide for Fingerprint explicitly says to maintain consistency with Android Settings. Such as “Confirm fingerprint”.  The BiometricManager tells us if a user “canAuthenticate()“, but doesn’t tell us what types of Biometrics are available on the device or which one (if more than one) is currently enabled.  The rationale for this:

If new sensors are developed, we would need to keep updating the “type” list, and apps would also need to keep updating to use the new types. Perhaps there’s a way to make that work, just we haven’t spent much time investigating.
– Googler’s Response on Issue Tracker

I think this is a great way to do it, and aligns with user expectations, but this is not available with current Biometric APIs. 😞

Pixel 3 Settings Pixel 4 Settings

Conclusion

This all sounds like a lot of work.  You can just use “Biometric” and you’ll probably be fine.  Users will get used to it eventually, right?  No matter how hard we try at this point, we will end up having to use that terminology in the cases where “Multiple” or “Unknown” biometric features are available anyways.

It kinda stinks that we got forced to use these APIs since FingerprintManager is deprecated, and the Biometric APIs have a lot of these little workarounds you may need to do.  I understand the rationale behind it from an OS standpoint, but I hope that Google exposes the type(s) of Biometric available on the device to use.  That way we are sure, and aren’t doing all this work to try and figure it out.

Recommendations

  1. Must: Use the AndroidX Library.  It’s a wrapper on top of the Android OS APIs and deals with specific workarounds, as well as provides a FingerprintManager fallback for devices prior to API 28 which don’t have a BiometricPrompt in the OS.
  2. Recommended: Checkout Biometricks which is a library in development to do what is mentioned in this article.  It has a sample app and more.
  3. Recommended: Do some user testing.  I’m giving some advice from what I’ve seen, but you may find something different with your users.  Your users are your source of truth.

Disclaimers

  • This is a UX guide, and not anything related to security of using Biometric features of Android.
  • These are my personal observations and opinions.

Related Links

SQLDelight 1.x Quick Start Guide for Android

SQLDelight is most well known as a Kotlin multiplatform database library. As an Android Developer, the most compelling reasons to use SQLDelight are:

  • Kotlin first
  • SQL first
  • Typesafe generated code
  • Unit tests don’t require an Android device

Documentation for how to use SQLDelight on just Android (without Kotlin mutliplatform) is lacking, so I wanted to create this guide to get people going fast on just Android.  This example is here to get you started, but does not reflect best coding practices.  I did this in order to make this quick start guide concise, and approachable.

1) Add Gradle Plugin to the buildscript classpath

In your project’s root build.gradle file, add the following dependency under buildscript dependencies.

classpath "com.squareup.sqldelight:gradle-plugin:1.3.0"

2) Apply the Gradle Plugin to your App or Module

You need to add the SQLDelight Gradle Plugin to your app or module because SQLDelight uses code generation from SQL files (*.sq), instead of code generation based on annotations (kapt).  This gives us incremental builds, but requires us to add a plugin to our root build.gradle file.  By doing code generation from SQL files, SQLDelight can generate code that works on any platform, because at the end of the day you are just executing SQL statements.

apply plugin: "com.squareup.sqldelight"

3) Add/Write the SQL (.SQ) File

You need to create a source folder for your SQL (*.sq) files at “src/main/sqldelight” folder, next to “java” or “kotlin” source folder. In this example, we’ll be representing a “item” in a shopping cart (Example from Shopping App) and the ItemInCartEntity.sq file in the following location:

MODULE_OR_APP/src/main/sqldelight/com/handstandsam/sqldelightquickstart/ItemInCartEntity.sq

ItemInCartEntity.sq

CREATE TABLE itemInCart (
    label TEXT NOT NULL UNIQUE PRIMARY KEY,
    image TEXT NOT NULL,
    quantity INTEGER NOT NULL DEFAULT 0,
    link TEXT
);

selectAll:
SELECT *
FROM itemInCart
ORDER BY label;

insertOrReplace:
INSERT OR REPLACE INTO itemInCart(
  label,
  image,
  quantity,
  link
)
VALUES (?, ?, ?, ?);

selectByLabel:
SELECT *
FROM itemInCart
WHERE label = ?;

empty:
DELETE FROM itemInCart;

deleteByLabel:
DELETE
FROM itemInCart
WHERE label = ?;

4) Install the SQLDelight Android Studio Plugin

The Android Studio Plugin is not required, but is super helpful for syntax highlighting, code completion, warnings and navigation.

.sq file without the SQLDelight Android Studio Plugin

Adding the SQLDelight Android Studio Plugin

A .sq file after the SQLDelight Android Studio Plugin is Installed

5) Add the Dependency Used for in Memory Unit Tests

You need the JdbcSqliteDriver for Unit Tests. This allows you to run your database tests without Android device. This means blazing fast tests without the hassle of connecting a device!

Add the following Unit Test dependency:

testImplementation "com.squareup.sqldelight:sqlite-driver:1.3.0"

Note: You don’t have to write unit tests and could technically skip this and the next step, but do yourself the favor and write tests from the beginning.

6) Write Unit Tests

Now that everything is set up, you should write a unit test that runs on your computer to make sure it’s all working.  This is a huge benefit over the Room library that comes with Android Jetpack because we can completely decouple ourselves from knowing what Android is.  This allows us to verify our setup and to have a quick feedback loop to ensure everything is working.

Use the JdbcSqliteDriver for an in memory version of your database for unit tests to avoid state between tests. However, be sure to also call Database.Schema.create(sqlDriver) or your in memory tests will not work. In order to make sure I call it, I use an “apply” to make sure I do it along with creating an instance of the driver.

private val inMemorySqlDriver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY).apply {
    Database.Schema.create(this)
}

Here is the full ItemDatabaseTest.kt file containing the Unit Test.

package com.handstandsam.sqldelightquickstart

import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver
import org.junit.Assert.assertEquals
import org.junit.Test

class ItemDatabaseTest {

    private val inMemorySqlDriver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY).apply {
        Database.Schema.create(this)
    }

    private val queries = Database(inMemorySqlDriver).itemInCartEntityQueries

    @Test
    fun smokeTest() {
        val emptyItems: List = queries.selectAll().executeAsList()
        assertEquals(emptyItems.size, 0)

        queries.insertOrReplace(
            label = "Pineapple",
            image = "https://localhost/pineapple.png",
            quantity = 5,
            link = null
        )

        val items: List = queries.selectAll().executeAsList()
        assertEquals(items.size, 1)

        val pineappleItem = queries.selectByLabel("Pineapple").executeAsOneOrNull()
        assertEquals(pineappleItem?.image, "https://localhost/pineapple.png")
        assertEquals(pineappleItem?.quantity?.toInt(), 5)
    }
}

7) Add the SQLDelight Android Driver Dependency

Now that we have the plugin working and unit tests passing, we need to integrate with our Android app. Add this “implementation” AndroidSqliteDriver dependency on SQLDelight.

implementation "com.squareup.sqldelight:android-driver:1.3.0"

If you only have an “app” module, then add it to that, but if you are in a multi-module project, I would highly suggest creating a “db” module (or similar) for this code.

8) Write your SQLDelight Android Code

In order to just show this working on Android, I pasted the code into the MainActivity, which is not what you should do, but it helps you validate that it’s actually working on Android.

You will need to use the AndroidSqliteDriver in order for SQLDelight to correctly write to the Android database. The JdbcSqliteDriver was helpful for allowing us to do in-memory unit testing, but it only keeps the database in memory, and would never save between app launches.

val androidSqlDriver = AndroidSqliteDriver(
    schema = Database.Schema,
    context = applicationContext,
    name = "items.db"
)

val queries = Database(androidSqlDriver).itemInCartEntityQueries

val itemsBefore: List = queries.selectAll().executeAsList()
Log.d("ItemDatabase", "Items Before: $itemsBefore")

for (i in 1..3) {
    queries.insertOrReplace(
        label = "Item $i",
        image = "https://localhost/item$i.png",
        quantity = i.toLong(),
        link = null
    )
}

val itemsAfter: List = queries.selectAll().executeAsList()
Log.d("ItemDatabase", "Items After: $itemsAfter")

9) Run The Code on Android

Hit the run button and filter Logcat so you can see that you have successfully added and retrieved data from SqlDelight on Android!

10) Peek at the “Magically” Generated Code

It’s cool to see where the plugin puts the code it generates in “build/sqldelight” and it may help you understand how SQLDelight works. The generated code is super easy to read since it uses Kotlin Data Classes, and the SQL code is just taken almost directly from your .sq file that you already wrote, but wrapped in a type-safe way.

Conclusion

These steps are all you need to get started with SQLDelight 1.x on Android.  Here is a pull request that contains all the changes mentioned in this post: https://github.com/handstandsam/SQLDelightQuickStart/pull/1/files.  This article was written when version 1.1.4 was released, but has been updated to version 1.3.0.  Check out the SQLDelight change-log to see the latest released version.

Enjoy the beautiful generated Kotlin code which is generated from our .sq files, and enjoy validating your code via unit tests that can run without an Android device!

Related Links:

Sharing Gradle Configuration in Multi-Module Android Projects

Using multiple modules in our Android projects help us split apart our code into logical components.  They also can enable faster incremental builds, and more modular code.  One problem with multi-module projects is that there is a lot of verboseness of configuration.  This post shows you a method of sharing common configuration between your Android Library modules in order to cut down on boilerplate Gradle configuration.

I made this change in a PR in my ShoppingApp project on GitHub and ended up deleting a net 90 lines of code over 7 library modules.

apply from: “____.gradle”

You can add the contents of another Gradle file into your current one by using “apply from: ” and specifying the file whose content you want to add.

apply from: "$rootProject.projectDir/android-library.gradle"

$rootProject.projectDir

Modules can exist in different directory structures, so by leveraging the $rootProject.projectDir property, we specify paths based on the root project directory.

Original Library Module build.gradle

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

android {
    compileSdkVersion Versions.compile_sdk

    defaultConfig {
        minSdkVersion Versions.min_sdk
        targetSdkVersion Versions.target_sdk
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
}

dependencies {
    implementation project(Modules.models)
    implementation Libs.kotlin_std_lib
}

Resulting Library Module build.gradle

apply from: "$rootProject.projectDir/android-library.gradle"

dependencies {
    implementation project(Modules.models)
    implementation Libs.kotlin_std_lib
}

Shared Gradle File

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

android {
    compileSdkVersion Versions.compile_sdk

    defaultConfig {
        minSdkVersion Versions.min_sdk
        targetSdkVersion Versions.target_sdk
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
}

If You Do Something 3+ Times, Extract Out Functionality

Whenever it’s possible and makes sense, use common configuration to reduce boilerplate.  This same rule applies if you are writing code, or writing Android Gradle configuration.  This post shares an “easy win”, that you may be able to use to help better manage your multi-module project. There is so much more you can do to clean up your builds by leveraging buildSrc where you can write in Kotlin, Java or Groovy, but that’s for another post.

“It Depends” Is The Answer To Your Android Question

Android Questions:

  • Should I use Mockito? “It Depends”
  • Should I put my Kotlin code in src/main/java? “It Depends”
  • Should I use Flutter? “It Depends”
  • Should I wrap a 3rd party library’s API? “It Depends”
  • Should I install the Android Q Beta? “It Depends”
  • Should I use Dagger? “It Depends”
  • Should I use Kotlin or Java? “It Depends”
  • Should I use React Native? “It Depends”
  • Should I use Dependency Injection? “It Depends”
  • Should I use OkHttp? “It Depends”
  • Should I use Multiple Activities or a Single Activity? “It Depends”
  • Should I upgrade to the latest Support Library? “It Depends”
  • Should I use a library that’s in Alpha? “It Depends”
  • Should I use Room or SqlDelight? “It Depends”
  • Should I write Espresso tests? “It Depends”
  • Should I bump my minSdk to 28? “It Depends”
  • Should I use MVP or MVVM or MVI or MVC? “It Depends”
  • Should I use RxJava or LiveData? “It Depends”
  • Should I learn Android or iOS? “It Depends”
  • Should I start a blog? “It Depends”
  • Should I start a side project? “It Depends”
  • … Insert Your Question Here  … “It Depends”

“It Depends” Is Technically Correct 💯% of the Time

While “It Depends” is technically correct since there is no absolute answer in software, it still doesn’t make it a good answer.  “Probably Should” is an answer well, but when you think about it, it’s still a variation of “It Depends”.

“It Depends” Is a Crappy Answer

Juhani brings up a great point about responsibility in his tweet above.  Opinions are great because they are shaped by experience.  As you gain experience, it’s important to share your opinions and discoveries because they will help provide insight and context into a topic.  That empowers the person searching for the answer to make a decision, because you can’t write software with “It Depends”, since it won’t compile. 😂

There Is No Perfect Answer

My goal of this post was to point out that there are tons of ways to do things, but based on your team, use case and target audience, there is no good 100% right answer for any topic.  I can strongly urge you to use Kotlin, but if you are building something that people are willing to pay millions of dollars from and they need Java, then Java is your right answer.  The important part is to keep listening to opinions and discoveries that are brought up, but know they are not a one-size-fits-all solution.

I’m happy to share my opinion and experiences with you on any topic if you reach out to me on Twitter @HandstandSam, and I’ll do my best not to answer with “It Depends”. 🙂

When You Should Use Null in Kotlin

I was recently reviewing code with a developer that is learning Kotlin and they were adamant that:

“You should never use null. Null is BAD.” 


Null has got a bad rap.  Yes, it’s to be avoided in most code, but in Kotlin, null is part of the type system and the compiler will tell you when you haven’t handled it appropriately.  However, just because it’s easy to identify null in Kotlin doesn’t mean we should use it everywhere.  There are perfectly appropriate safe uses of null in Kotlin.

Valid usages of null in Kotlin

1) When Representing “No Value”

 var user: User? = null

If you need a way to represent whether a value is initialized or whether it has no value, then null is appropriate in this case.

2) Consuming External Data Sources

If you need a way to represent whether a value is available or unavailable, then null is appropriate in this case as well.

Apps must consume external data sources that we don’t have full control over.  When operating in a non-hermetic environment, we run into cases where some data sources are not reliable and may provide null content.  Whether it is a REST API, or an Android System Service, representing data that may not be available as a nullable type is important because it allows us to create code paths to handle both cases.

Functional Purity

While it seems ideal to never have null in your code, it’s not always possible to avoid, and sometimes shouldn’t be avoided.

Using a Sealed Class to Avoid Using Null

sealed class UserWrapper {
  object UninitializedUser : UserWrapper()
  data class InitializedUser(val user: User): UserWrapper()
}

Creating a sealed class for the sole purpose of avoiding the use of null introduces unnecessary boilerplate code.

Don’t get me wrong, sealed classes are one of my favorite features of Kotlin though, and are great if you need to handle more scenarios than just initialized or uninitialized, but if that’s all you need, then using null is a valid approach.

Using a Nullable Field

val user : User?

Using a nullable field is much more concise in your definition than a sealed class.  It’s also more concise to use a ?. operator instead of an “if” or “when” statement to process your sealed class.

Offensive Programming

If you really want to, you can run around with a bunch of !! (double bangs) in your code.  However, you are living dangerously and your code will crash if the value is null.  This may be what you are looking for though if you are trying to follow practices of Offensive Programming.

//This will crash if the user is null
handleNonNullUser(user!!)

Defensive Programming has us cover all possible code paths just in case it might occur.  When you do have these extra code paths, provide some fallback logic or warning logs.

Ways of Handling Null

If you check once that the value is non-null, the Kotlin compiler is smart enough for you to write your code without providing any !!.  Then process the non-null val after it’s been checked once, to avoid multiple checks throughout my code. Here are some ways to do this:

Null Comparison 😐
if (user != null) {
  handleNonNullUser(user)
} else {
  Log.w(TAG, "user was null")
  handleNullUser(user)
}

This is very Java-esque, but it does the trick.

Let 🙂
user?.let {
  //Work with non-null user
  handleNonNullUser(user)
}

I like using let because it allows us to write much more idiomatic Kotlin.  It does not allow us to handle null, “else” case though.

Early Exit 👍
fun handleUser(user : User?) {
  user ?: return //exit the function if user is null
  //Now the compiler knows user is non-null
}

If the value is null, you can exit the method immediately, otherwise you can operate on the non-null value (because the Kotlin compiler is that smart).

Immutable Shadows 👥😲

Using immutable “variable shadows” a really cool way of providing null safety.  It will do an “Early Exit” if the value is null, otherwise it will assign the non-null value of the var to an immutable val allowing the compiler to know the value is non-null.  Thanks to Gabriel Peal for this tip!

var user : User? = null

fun handleUser() {
  val user = user ?: return //Return if null, otherwise create immutable shadow
  //Work with a local, non-null variable named user
}

Conclusion

There is no doubt that you will write better code if you try and stick with non-null and vals where you can. However, if you run into a scenario where using null is the right thing to do, then don’t fight it.

Code appropriately using one of the techniques I’ve shared in this article to handle null effectively.  These techniques allow you to avoid having to use the !! operator which can lead to NullPointerExceptions.  The whole point of having a nullable type in Kotlin is to allow you to avoid NullPointerExceptions at compile time, instead of them sneaking up on your at run time.

You may be thinking that this post is similar to Roman Elizarov’s post “Null is your friend, not a mistake“, and you would be right.  His post inspired me to finally publish this, and you can read more of the story behind publishing this post here.

Hitting Publish is the Hardest Part of Blogging

I had been sitting on the draft of my post “When You Should Use Null in Kotlin” since November (for 3 months) because after I had a colleague review it, I wasn’t sure if my views were valid.  My colleague told me about “null, the billion-dollar mistake” and asked if I was just trying to “stir the pot” with a possibly unpopular opinion. That made me question if my views were actually valid, so I didn’t hit “Publish”.

Draft was Last Modified 3 Months Ago

Validation

Earlier this month, Roman Elizarov (@relizarov), from the Kotlin libraries team posted an article which validated my thoughts I shared in my draft post, null is something to use, and not completely avoid:

I was excited to see a like-minded post from one of my software idols who designs the Kotlin language:

Chatting with Roman at Google I/O 2018

Thanks to this validation, I finally got up the courage to finish my post which took many hours and reviews, but I finally published my article. 😄 Thanks Roman (@relizarov)!

Non-null Opinions

I encourage you to share your opinion on topics where you have spent enough time digging into a topic to develop one.  So much of computer science is subjective, and there will always be varying opinions.  As long as you have identified reasons why you feel a certain way, those are valid.

It’s okay to share your thoughts.  Your opinion is non-null 😂.  Be humble enough to listen to others and allow yourself to use objective reasoning to help shape your understanding.

Leverage The Community

The Android community has varying opinions on many things, but that isn’t a bad thing.  There is no single way to do something, and whether or not something is the right solution depends on the use case, and the skills of the team.

Many members of the community are there to help.  I encourage you to reach out and ask for feedback from more than one person as it will help you get well rounded feedback.  Before I published the post I got feedback from @molsjeroen, @ataulm, @ZakTaccardi, @gpeal8 and @jarvisapps.  Each person had slightly different insights, and it helped me create a higher quality post.

A Checklist for Hitting Publish

  1. Write your content
  2. Add applicable content/images/code
  3. Review your content as if you were a reader
  4. Ask for reviews from 2+ people
  5. Address feedback
  6. Set a time limit (3? days)

I put together this checklist to help guide me in the future, and hope it will allow me to publish more posts!  I have at least 5 more drafts in my head that I need to get to next.  By publishing these articles, I’m able to get to the next one, and hopefully help someone publish theirs.