Implementing optional callbacks in Kotlin from scratch – LogRocket Blog

Boemo is a software developer who embraces innovative approaches. He likes diving deep into complex concepts in order to learn and write articles that can help the reader understand complex methodologies in a simple and fun way.

Task results are very important in a program, because they determine if the next task will be executed. The best programs are the ones that are able to poll and fetch results automatically and synchronously.

Android has a mechanism called optional callback that allows your methods and classes to receive the failure and success results of another task without having to call another class time and again just to get results.

In this tutorial, we will learn about optional callbacks and how to implement them in Kotlin. In addition, we will get to explore libraries that implement this functionality for you.

Contents

What are optional callbacks?

Android callbacks allow your method to fetch results from another method synchronously. Callbacks act as messengers; they are the center of communication in a program, and get passed into functions as arguments. They help in completing the task of a function by providing results, either negative or positive. An optional callback explicitly states what will happen when different results are fetched from another method or function.

Optional callbacks also allow functions that rely on a callback to continue even if the callback returns an error, which supports high level maintenance and reliability.

Optional callbacks are important, because they make sure that users can enjoy your app without interruption by preventing the application from freezing and blocking. A freezing UI can lead to many people uninstalling your app.

What’s the difference between optional and standard callbacks?

As an Android developer, I use callbacks on a daily basis, starting from the onCreate() method, which creates an activity when an application is launched. This is a typical example of a callback you will use a million times in your Android development journey.

When your app crashes at start time, the problems can be found in this callback:

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setContentView(R.layout.activity_main)
}

Here is another example of a typical callback that is called when the activity is paused:

override fun onPause() {
        super.onPause()
        print("onPause")
    }

The above callbacks do a great job in the Android activity lifecycle and fragment lifecycle. But, the problem arises when the method implemented in the onPause() callback does not work. An optional callback would state what would happen if that error occurs.

Implementing optional callbacks in Kotlin from scratch

Using optional callbacks in your program should not be optional. Optional callbacks keep your activity flowing even when exceptions occur and tasks output negative results.

Now, here is an example of an optional callback:

Result.of<T, Throwable>(doTask())
      .flatMap { normalizedData(it) }
      .map { createRequestFromData(it) }
      .flatMap { database.updateFromRequest(it) }

Add the failure and success cases:

//if successful
result.success {
}

//if failed
result.failure {
}

The above optional callback has conditions: the failure and success switch. You have to state what happens if the results from another class are negative or positive. In this way, you will be able to close processes and functions that may crash the app when the callback returns a negative result.

Here is another example of an optional callback that does not use a library. The optional callback shown below provides three cases that will be triggered when results are passed. You are given the opportunity to do something if the animation repeats, ends, or starts:

animation.setAnimationListener(object : Animation.AnimationListener {
   override fun onAnimationRepeat(animation: Animation?) {

   }

   override fun onAnimationEnd(animation: Animation?) {
       doSomething()
   }

   override fun onAnimationStart(animation: Animation?) {
          }
})

An optional callback structure mainly consists of a code block that fetches results, which is followed by the failure and success case (or any case you would like to be executed when certain results are received). However, you can add other cases if you are implementing them starting from scratch without an intervention of a library.

A library (like Result) makes it easy by giving you two cases: failure and success. All you have to do is add code that will be added when a success or failure instance occurs.

The failure and success cases are what makes optional callbacks different from typical callbacks:

//if successful
result.success {

}
//if failed
result.failure {

}

Implementing optional callbacks in Kotlin using Result

Result is a Kotlin library that allows you to implement optional callbacks and helps you handle errors swiftly.

To get started, add the following dependencies to the gradle.build file and synchronize Gradle to download the third-party library:

implementation("com.github.kittinunf.result:result-jvm:5.2.1")
implementation ("com.github.kittinunf.result:result:5.2.1")

Let’s see how Result works in a case where we want to open a text file and read it. To get the result of opening the text file task we use the Kotlin Result object, which fetches the success and failure value of the readText task:

val operation = { File("/path/to/file/foo.txt").readText() }
Result.of { operation() }  

Next, we create a normalizedData(foo) function that sorts and simplifies data collected from the operation. Normalize the data by calling the normalize() function:

fun normalizedData(foo): Result<Boolean, NormalizedThrowable> {
    Result.of { foo.normalize() }
}

Next, create a request from the data we normalized above:

fun createRequestFromData(foo): Request {
    return createRequest(foo)
}

After requesting data, create a database function as shown below that will update the values of the Result object. If the updating transaction fails then an exception will be thrown:

fun database.updateFromRequest(request): Result<Boolean, DBThrowable> {
    val transaction = request.transaction
    return Result.of { 
        db.openTransaction {
            val success = db.execute(transaction)
            if (!success) {
                throw DBThrowable("Error")
            }
            return success
        }
    }
}

After you have fetched information using the Result object, it’s time to add the success and failure cases. Start by declaring the value:

val (value, error) = result

Now, get the result value:

val value: Int = result.get()

Now add the success and failure cases:

result.success {
//Add code that does something when the task is successful here
}

result.failure {
//add code that warns the user that an error has occurred here.
}

Implementing optional callbacks in Kotlin using callback-ktx

The callback-ktx library wraps and transforms callback-based APIs into suspending extension functions. Currently, callback-ktx only supports the following APIs:

  • Animation
  • Location
  • RecyclerView
  • Sensor
  • View
  • Widget (TextView)

Use the following code to download the callback-ktx dependency:

implementation("io.github.sagar-viradiya:callback-core-ktx:1.0.0")

After you have downloaded the library, You can track animations by invoking the lifecycleScope object to get the scope of the life cycle. Next, call the awaitStart() method, which will be triggered when the animation.

Now, you have the opportunity to state and specifically add code that will be executed only when the animation starts:

viewLifecycleOwner.lifecycleScope.launch {
  animator.awaitStart()
  // insert code that does something when the animation starts here.
 }

You can check out more use cases of the callback-ktx library here. If you still have an Android codebase that uses Java you can use Vulture to implement callbacks safely.

Conclusion

In this article we have learned about the benefits of optional callbacks, the difference between optional and regular callbacks, and how to implement callbacks using Result and callback-ktx.

Optional callbacks will surely give you more ability and choice when it comes to stating what should happen when errors and success results are returned by the callback.

If you have any questions or comments, feel free to leave them in the comment section below.

LogRocket: Full visibility into your web and mobile apps

LogRocket Dashboard Free Trial BannerLogRocket Dashboard Free Trial Banner

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.

Try it for free.

Share this: