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.
Most of the time, listening for events inside
DialogFragment is not a trivial task. There are things to consider:
- A listener can be an
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.
- 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.
- Other than listening, sometimes we are required to send data to it, in order to prevent redundancy in writing business logic.
🌟 Characteristics of
There are some characteristics of
DialogFragment that we need to understand before we discuss the solution for our problem:
Dialog, by invoking
Dialogitself render a view that is defined by
onCreateView(). You may override these methods to create your own
- It handles created
Dialog’s lifecycle, therefore, you don’t need to clear reference to the
- It is attached to
FragmentManagerof 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
dismissAllowingStateLoss()to close a
There are 3 solutions that I propose. The third one is my choice.
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.
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.
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.
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
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
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
ViewModel can also be used to send data from host to
DialogFragment. I’m calling
ViewModel in this mechanism as
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
- 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
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.