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 Kotlin Multiplatform? “It Depends”
  • Should I use Kotlin Multiplatform for UI? “It Depends”
  • Should I use an Actor or StateFlow? “It Depends”
  • 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.

Why We Need “fat” AARs for Android Libraries

I want the ability to create a single (“fat”) AAR artifact from multiple Android Libraries (all from source).  Non-source, transitive dependencies will still be pulled in via a pom.xml file.

Requirements:

  1. I want to write and SDK that would be used by 3rd parties and possibly internal teams.  I want to write modular code on my side, with clean separation of concerns, yet only provide a single AAR artifact to users of my library.  Users of my library don’t need to know how I architected the internals, and in some cases I don’t want them to know.  I want to obfuscate my internal implementations to avoid accidental usage as well as for some security.
  2. I want to shrink and optimize my code with ProGuard (which is being replaced by R8) on my entire library, and generate only a single AAR file.  With current tooling, each module is only aware of its own code and resources when ProGuard is run.  This means that I can’t optimize or obfuscate my entire library/SDK in its entirety.  Because I must run ProGuard on each module individually at the current time, you end up with NoClassDefFoundErrors if you try to be aggressive with obfuscation.

The Use Case

For this post, think of an SDK you would get from an external vendor, or another team that contains their shrink-wrapped code that you need to plop into your app.  For an app like Twitter, that could mean:

  • Login page library
  • Video streaming player library
  • Home feed library
  • Emoji support library
  • etc.

Note: With the Twitter example I just mean to show that you can bundle discrete components of an app with clear boundaries between them.

When you get into a big app, you have to separate out components, and using Android Libraries to do this is a great decision.  That being said, I would never want “fat” AARs to be the only way to do things.  I just think it would solve some use cases when you are creating and SDK to be used by 3rd parties that don’t need to know how the internals of your code work.

Technical Reasons: Why Creating a “FAT” AAR Doesn’t Work

Apps/APKs can combine as many AARs into a single APK artifact.  That’s because there is a Manifest merge process that defines rules on how the AndroidManifest.xml, resources and assets are merged for the resulting APK file.  At the time the APK is created, ProGuard can be applied to optimize all byte code, remove unused classes, and perform code obfuscation.

Didn’t Someone Create a Library to Do This?

Kinda… A long time ago.  There was a partial workaround for manifest merging for Android Libraries using Android Gradle Plugin 2.x called “FAT” AAR, but it was limited, unofficial, and no longer works with Gradle 3.x.  There seems to be no plans to make it work with newer versions of Gradle.  It esentially tried to do its own hacky version of Manifest merging.

Argument Against: You only need to specify a single dependency in your build.gradle to import all of the transitive libraries. Why are you complaining?

This is true for the use case where you publish your artifacts correctly to a public Maven repository.  Your user would only need to care about adding a single Gradle dependency, and the others would be pulled down transitively.  All they would need to add is “com.example:my-lib:1.0.0” to their Gradle dependencies. This works great in a lot of cases, like the Android Support Library, where the dependencies are all published publicly, and users can cherry-pick the pieces they want.

In the use case I’m requesting this for, existing methods aren’t great for 2 reasons.

  1. I want to control what my shrink-wrapped artifacts look like, while still maintaining modularized code internally.  It’s easier to hand over a single AAR file when you need to distribute a library via a non-public Maven repository. Yes, I could ask users to create a file-based maven repository in the project, but that is ugly.
  2. I want to shrink and optimize all of my library before delivering it to users.  It is only possible to run ProGuard on each module individually at the current time, which means that code optimizations and obfuscation breaks when it is used aggressively on each module independently.

Finally

I’ve created this post to explain some of the current limits of building Android Libraries with the Android Gradle Plugin that I’ve run into, and to make a plea to the Android Tooling team to accept the issue for newer versions of Android Gradle Plugin.  ⬇Xavier Ducrohet, the Android SDK Tech Lead said this is being considered for Android Gradle Plugin 3.3, but isn’t on the roadmap yet.  Since 3.2 is almost out the door, I figured now is a good time to make a push for this issue.

If you have this same issue or use case, please ⭐ the issue, but also comment about how this uniquely effects your development.

Kotlin + buildSrc for Better Gradle Dependency Management

Multi-module Android projects are now the recommended way to take advantages of performance improvements with Android Gradle Plugin 3+.  However, as we add more modules to our project, we quickly run into the issue of dependency management.

Different ways of managing Gradle dependencies:
  1. Manual Management
  2. Google’s Recommendation using “ext”
  3. Kotlin + buildSrc

1) Manual Management 👎

This is the way most of us have been managing dependencies, but it requires a lot of manual changes whenever you upgrade a library to ensure that versions are updated correctly.

module_a/build.gradle

implementation "com.android.support:support-annotations:27.0.2"
implementation "com.android.support:appcompat-v7:27.0.2"
implementation "com.squareup.retrofit2:retrofit:2.3.0"
implementation "com.squareup.retrofit2:adapter-rxjava2:2.3.0"
implementation "io.reactivex.rxjava2:rxjava:2.1.9"

module_b/build.gradle

implementation "com.android.support:support-annotations:27.0.2"
implementation "com.android.support:appcompat-v7:27.0.2"
implementation "com.squareup.retrofit2:retrofit:2.3.0"
implementation "com.squareup.retrofit2:adapter-rxjava2:2.3.0"
implementation "io.reactivex.rxjava2:rxjava:2.1.9"

This is a lot of duplicated configuration that is hard to manage upgrades with, especially when you have a lot of modules.

2) Google’s Recommendation: Using Gradle Extra Properties 🙂

This is Google’s recommended way of doing this as seen in the Android documentation.  It is also used in lots of Android projects, like ButterKnife and Picasso.

This method is great for upgrading libraries like the support library.  Every support library dependency has the same version number, so only having to change this in one place is 💯.  The same things goes for Retrofit, and many other libraries.

Root-level build.gradle

ext {
  versions = [
    support_lib: "27.0.2",
    retrofit: "2.3.0",
    rxjava: "2.1.9"
  ]
  libs = [
    support_annotations: "com.android.support:support-annotations:${versions.support_lib}",
    support_appcompat_v7: "com.android.support:appcompat-v7:${versions.support_lib}",
    retrofit :"com.squareup.retrofit2:retrofit:${versions.retrofit}",
    retrofit_rxjava_adapter: "com.squareup.retrofit2:adapter-rxjava2:${versions.retrofit}",
    rxjava: "io.reactivex.rxjava2:rxjava:${versions.rxjava}"
  ]
}

module_a/build.gradle

implementation libs.support_annotations
implementation libs.support_appcompat_v7
implementation libs.retrofit
implementation libs.retrofit_rxjava_adapter
implementation libs.rxjava

module_b/build.gradle

implementation libs.support_annotations
implementation libs.support_appcompat_v7
implementation libs.retrofit
implementation libs.retrofit_rxjava_adapter
implementation libs.rxjava

This is a huge step ahead from manual management, but IDE support is lacking.  Check out this screencast of migrating to use Gradle Extra Properties (“ext”), and this Github pull request of the results.

While you can be content with using “ext” properties, I think you will be excited to use Kotlin in a buildSrc directory.

3) Kotlin + buildSrc == Android Studio Autocomplete 😎 🎉

You can create a buildSrc module with Kotlin code to manage dependencies and get IDE completion support.

From the Gradle Documentation:

When you run Gradle, it checks for the existence of a directory called buildSrc. Gradle then automatically compiles and tests this code and puts it in the classpath of your build script. You don’t need to provide any further instruction.

You just need 2 files in your buildSrc module:

  1. build.gradle.kts
  2. Kotlin Code (In this case,Dependencies.kt)

buildSrc/build.gradle.kts

plugins {
    `kotlin-dsl`
}

buildSrc/src/main/java/Dependencies.kt

object Versions {
    val support_lib = "27.0.2"
    val retrofit = "2.3.0"
    val rxjava = "2.1.9"
}

object Libs {
 val support_annotations = "com.android.support:support-annotations:${Versions.support_lib}"
 val support_appcompat_v7 = "com.android.support:appcompat-v7:${Versions.support_lib}"
 val retrofit = "com.squareup.retrofit2:retrofit:${Versions.retrofit}"
 val retrofit_rxjava_adapter = "com.squareup.retrofit2:adapter-rxjava2:${Versions.retrofit}"
 val rxjava = "io.reactivex.rxjava2:rxjava:${Versions.rxjava}"
}

After we have done a Gradle Sync, following making the changes above, we can now access any of the values in Android Studio.

The result looks very similar to what “ext” looked like, but we have autocomplete and click support (to take you to the definition).

module_a/build.gradle

implementation Libs.support_annotations
implementation Libs.support_appcompat_v7
implementation Libs.retrofit
implementation Libs.retrofit_rxjava_adapter
implementation Libs.rxjava

module_b/build.gradle

implementation Libs.support_annotations
implementation Libs.support_appcompat_v7
implementation Libs.retrofit
implementation Libs.retrofit_rxjava_adapter
implementation Libs.rxjava

Check out this screencast and this Github pull request showing migration from Gradle Extra Properties “ext” to use Kotlin + buildSrc.

Conclusion

I highly recommend the “Kotlin + buildSrc” option.  It may not seem like it’s that big of a deal, but managing Gradle dependencies is a pain, and having autocomplete and click support is a game changer.  No more switching back and forth between files manually!

Related Caster.io Lessons on Gradle Dependency Management (FREE)

  1. Gradle Dependency Management: Using Gradle Extra Properties (ext)

  2. Gradle Dependency Management: Using Kotlin and buildSrc for build.gradle Autocomplete in Android Studio

Related Links

Hat Tips/Thanks

Questions/Comments?

Reach out to me on Twitter at @HandstandSam