ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Follow publication

Kotlin Granular Analytics With Lifecycle Delegate

First of all let’s answer the question, why and what you’ll be analysing?

Michal Ankiersztajn
ProAndroidDev
Published in
3 min readAug 19, 2024

I’ll use Android as an example, but it can also be used for broader cases. Let’s say there is a bug in your app that sits for a long time, and you just can’t find where it happens, or maybe your PM said it’s time for us to track when every screen is opened and closed. Let’s say that the screen is a Fragment :

Graphic from Android Developers

This means you’ll need to react to callbacks related to the View lifecycle, and it’s a …

Struggle

For us Android and Multiplatform developers creating analytics for screens is a struggle. More often than it should, projects ended with base classes for Fragments and Activities, which is an anti-pattern and a bad practice.

Previously, some of these ideas came from programming in Java ps it didn’t support composition through delegation like Kotlin does. However, there is simply no reason for that, as in Kotlin, you can get very granular information about the screen state through Lifecycle .

Solution

Let’s take a look at a potential solution with delegation:

interface ScreenTracker {
fun setLifecycle(name: String, lifecycle: Lifecycle)
}

class LifecycleScreenTracker(
// Pass your tracker here:
private val tracker: Tracker,
) : ScreenTracker, LifecycleEventObserver {
private var lifecycle: Lifecycle? = null
private lateinit var name: String

override fun setLifecycle(name: String, lifecycle: Lifecycle) {
this.name = name
this.lifecycle = lifecycle
lifecycle.addObserver(this)
}

override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
tracker.track("Screen $name: ${event.name}")
// Cleanup
if (event == Lifecycle.Event.ON_DESTROY) {
lifecycle?.removeObserver(this)
lifecycle = null
}
}
}

We’re using an interface ScreenTracker here instead of a normal class to enable delegation and create a more flexible approach as you might not always want to log everything, etc.

For simplicity, I’ll assume that Tracker looks like this:

class Tracker {
fun track(event: String) {
Log.d("Tracking", event)
}
}

In a real app, you’ll have Firebase Analytics or something like that in this place. Additionally, you should use dependency injection to pass the tracker.

Usage

To make it more usable, we’ll need to add an extension function for fragments:

fun ScreenTracker.startTracking(fragment: Fragment) {
setLifecycle(
name = fragment::class.simpleName ?: "N/A",
lifecycle = fragment.viewLifecycleOwner.lifecycle,
)
}

This way the setup in the Fragment is as easy as that:

class ExampleFragment : Fragment(), ScreenTracker by LifecycleScreenTracker(Tracker()) {
override fun onCreateView(...): View {
startTracking(this)
...
}
}

And that’s it. Here’s an example output from the Tracker when you open the screen and then minimize the app:

Screen ExampleFragment: ON_CREATE
Screen ExampleFragment: ON_START
Screen ExampleFragment: ON_RESUME
Screen ExampleFragment: ON_PAUSE
Screen ExampleFragment: ON_STOP

Reading it in debug is also powerful, but the crashes' analytic events are golden!

Activity

If you need to use it Activity it’s pretty straightforward as well. Just add another extension function:

fun ScreenTracker.startTracking(activity: Activity) {
setLifecycle(
name = activity::class.simpleName ?: "N/A",
lifecycle = activity.lifecycle,
)
}

// Usage example
class MainActivity : AppCompatActivity(), ScreenTracker by LifecycleScreenTracker(Tracker()) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startTracking(this)
...
}
}

With little modification, it can also be used to Compose. All you’ll need to do is pass in the current Lifecycle.

Thanks for reading! Please clap and follow me if you’ve learned something new!

Published in ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Written by Michal Ankiersztajn

Android/Kotlin Developer & Applied Computer Science Engineer

Responses (2)

Write a response