Lifecycle-Aware Interactor for DialogFragment

Erick Pranata
Jan 2, 2019 · 4 min read
Photo by Dev on Unsplash

Here’s a problem: show a DialogFragment, then listen for its events. What would you do?

This article will help you create a foolproof mechanism to listen, even sending signals, to DialogFragment while considering any Android lifecycles within.

You might want to read about Android Architecture Component before moving on because this article is heavily depended on components in it.

😕 Problem

Most of the time, listening for events inside DialogFragment is not a trivial task. There are things to consider:

  1. A listener can be anActivity or a Fragment, depending on which one is currently showing the Dialog. Therefore, writing code to inform listener on events should be as flexible as possible, as the listener may come from different types.
  2. Reference to the listener should also be brought across config changes, as every Android app is highly prone to these changes: orientation, language, and such.
  3. Other than listening, sometimes we are required to send data to it, in order to prevent redundancy in writing business logic.

🌟 Characteristics of DialogFragment

DialogFragment is a component of Android SDK that can be used to display a Dialog while managing its lifecycle. It is preferable to use DialogFragment than Dialog, as it is as functional but safer.

There are some characteristics of DialogFragment that we need to understand before we discuss the solution for our problem:

  1. DialogFragment displays a Dialog, by invoking onCreateDialog(). The Dialog itself render a view that is defined by onCreateView(). You may override these methods to create your own Dialog.
  2. It handles created Dialog’s lifecycle, therefore, you don’t need to clear reference to the Dialog whenever its Activity is finished.
  3. It is attached to FragmentManager of its host. It may belong to Activity, or a Fragment. Hence, dismissal of its dialog should also consider popping itself from the FragmentManager. Always use dismiss() or dismissAllowingStateLoss() to close a DialogFragment (don’t use dialog?.dismiss())

💡 Solutions

There are 3 solutions that I propose. The third one is my choice.

One

Get a reference to host Activity or Fragment, then invoke the corresponding method. This can be achieved if the host has implemented the listener Interface.

Listener
Listener Interface
Get Reference of Dialog’s Host

This approach is lifecycle-aware. But getting the reference requires knowledge about this fragment’s host: whether it is an Activity or a Fragment. On the other hand, we can’t send data from host to the dialog, as the host is only listening.

Surely there are workarounds on this approach, but I only fancy this on my early days as an Android Developer.

Two

Rather than getting the reference from the Fragment, send the listener reference instead. As every fragment instance should be created using Builder Pattern, it can be written like this.

The fragment has to use Builder Pattern

I’ve used this approach ever since. As it is more flexible than the first approach. Yet, config changes mess up the listener, as Fragment won’t persist anything other than its arguments. This approach also fails to be used in sending data to the Fragment.

Three

Introducing, ViewModel. A lifecycle-aware component that persists states, as long as it is alive. ViewModel will only be destroyed, if its scope is destroyed.

By combining it with LiveData or RxAndroid, this component can be used to keep data from DialogFragment, and be listened by any host that want to listen to it. Consider it as Publish-subscribe Pattern.

Not only sending data from DialogFragment, ViewModel can also be used to send data from host to DialogFragment. I’m calling ViewModel in this mechanism as Interactor.

🙊 Interactor

Borrowing terminologies from Flutter BLoC pattern, Interactor should only contain Sinks and Streams.

  • Sinks are used to receive data from any source. May it be from the host, or the DialogFragment.
  • Streams, on the other hand, are used to emit data to any source.
  • Sinks may also become Streams if the data that Sinks received don’t any further process before being emitted by the Streams.

This approach proposes a Reactive Programming paradigm.

It is lifecycle-aware, as it’s using ViewModel and LiveData. And anything can send/receive data to/from it.

And that’s it.

I think I’ll stick to this approach, as I assume, this by far is the best pattern for listeners in Android. Any comments?

ps: a working code can be found here. Feel free to discuss this approach.

Renseki

Renseki BTS: technological point of view