From Fragments to Activity: the Lambda Way
Activities have their lifecycle, and the same goes for
Fragments. Hence, communication from
Activities is notoriously problematic.
The communication between
Activities cannot be as simple as passing a listener to the fragment that the activity could implement: the
Activity would soon rotate and the
Fragment would keep a reference to an
Activity that had died and would leak. Google recommends a “good old pattern” to achieve this goal and limit the risk of leaks. In this article, we will demonstrate that lambdas are a better solution, more expressive, and easier to maintain.
We have created a Github repo that contains the code of our solution: https://github.com/stephanenicolas/activtity-fragment-lambda
The Good Old Way
The official way to make a Fragment communicate with its hosting Activity involves creating a communication interface and to implement it in the
Fragment will get a reference to its hosting
Activity and use an ugly and risky cast to communicate to the
Activity via the communication interface:
There are various issues with this approach:
- the casting statement is ignominious and fails at runtime.
- you don’t see clearly how the
Activityare linked. The link is more implicit than explicit.
This makes the Good Old Way crash-prone, and hard to maintain. Let’s try something different!
The Lambda Way
The Lambda Way will favor composition over inheritance. We don’t want our
Activity class to implement the communication interface anymore:
Our communication interface is modified to use any
Activity. Thus it becomes independent of the
Activity instance and won’t leak it:
We will see below why it uses a generic and it is
Fragment will pass its current hosting
Activity instance to the listener when it wants to communicate with it:
And here’s he key: the activity will now set a listener to the
Fragment, using a simple method reference.
What is this method reference :
This is one of the tough questions of this approach, and it gives us an opportunity for a good dive into the Java syntax.
MainActivity::onArticleSelected is an ambiguous java statement. It can represent:
- either a reference to a static method;
- OR a reference to non static method of object of a specific type.
onArticleSelected was a static method of
MainActivity, we could refer to it via
MainActivity::onArticleSelected . Referencing a static method is the most typical use of this type of reference.
But, in our case,
onArticleSelected is **not** static. So, we would usually refer to it as
mainActivityInstance::onArticleSelected . This method reference would point to the non-static method
onArticleSelected . You could use, for instance, in a Rx chain to map an observable of positions:`
So, what does
MainActivity::onArticleSelected refer to ??
Internally, all non-static methods in Java are compiled in the same way as static methods, but with one hidden first parameter of the type of its enclosing class. Thus, the 2 methods below are almost equal from the byte code perspective:
The method reference
MainActivity::onArticleSelected is a reference to the second form of this method. It is now pointing to a method that takes 2 parameters: a
MainActivity and a
This second method is interesting for us because it is independent of the
MainActivity instance. Hence, a reference to it doesn’t leak the activity instance!
From a lambda perspective, the meaning of this method reference is now slightly different: it is indeed a method that accepts a
MainActivity and invokes the method
onArticleSelected on this activity:
MainActivity::onArticleSelected is equivalent to
activity -> ((MainActivity)activity).onArticleSelected(position)
In other words,
MainActivity::onArticleSelected is a method reference to a
HeadLineListener implementation that is independent of the activity and will work with any instance of
For a fine-grained explanation of method references, please visit the official Java documentation.
As explained above, this method reference can be misunderstood by the Java compiler. It could understand it as a static method reference. In order to disambiguate it, both the interface
HeadlineListener, and the method
HeaderFragment#setHeadlineListener will use generics to trigger what is called a target type inference:
Thanks to these 2 generics, the compiler understands that the future method reference passed to
setHeadlineListener as a reference to a non static method of object of a specific type, and it will only accept references that point to a method inside a class that extends
Activity . This last constraint is reasonable because, from a
Fragment perspective, we can easily get the hosting Activity using
getActivity(). This is why we can now invoke our listener method on the current
Surviving the Fragment Lifecycle
The last point is to make sure that our fragment still knows about its callback when it dies and it is recreated by the
FragmentManager (e.g. after a rotation). For this, we will serialize the listener into the arguments of the fragments.
In our Github repo, we opted for a very structured approach to create our
HeadlineFragment by using a builder pattern. The builder will let you create the fragment with an appropriate listener. Of course, the builder is not mandatory for the “lambda way” to work, you just need to serialize the lambda and you can do it the way you want.
It is generally advised not to serialize lambdas in Java, exactly for the same reasons as inner classes. Nevertheless, this issue is softened here as:
- on Android, serialization is short term and cannot create issues during application upgrades and class changes.
- the lambda/method reference is fully static and stateless, and is intrinsically protected from any serialization issue as it can’t contain any reference to a non serializable element nor refer to entities that could have died (e.g. the
This new approach is quite technical, we agree on this. It also infringes the general rule of not serializing lambdas. However, we believe that this approach brings a few benefits to our code base:
- The link between Activities and Fragments is now explicit. It can be traced easily inside our IDE as opposed to implementing an interface the Good Old Way.
- We got rid of the hideous casting of the Good Old Way.
- We favor composition over inheritance.
- Code looks quite close to the rest of our modern Rx chains.
- When using a
Fragmentwith such an API, using the listener interface is as easy as using a method reference and most of the complexity of the solution goes behind the scene.
- It enforces building fragments using a builder pattern to setup the arguments which reduces bugs at runtime.
Thanks to Samuel Guirado Navarro for bullet proofing the approach.
Let us know what you think, and thx for reading this article !
With 💚 , the Groupon Android team.