IAN LAKE: Thanks for coming.
My name is Ian Lake.
JEREMY WOODS: And I'm Jeremy Woods.
IAN LAKE: And today we're here to talk
to you about Fragments--
not just Fragments of today, but Fragments of past,
present, and we're going to give you
a little sneak peek about where we're going
with Fragments in the future.
So starting with the past, Fragments
were introduced in Android Honeycomb API 11.
But we don't really talk about Honeycomb.
So we'll move on to what they were originally supposed to do.
And Fragments were really designed from the beginning
to kind of be micro activities.
Right?
Remember way back in the day when everything
was within your Activity?
And Fragments were really kind of that very first step
into moving code out of your activity
and into something smaller.
But that also meant that we inherited
a lot of the API service that was inherent in Activity.
So that means that we got things like all the action bar menu
stuff because there wasn't something called Toolbar yet.
It also meant that we had things like context menus, which
does anyone actually use context menus anymore?
They're a thing.
But they don't really need to be on activities or Fragments,
per se.
They could be on views themselves.
It also meant that we got a lot of custom hooks.
So all the things that were normally
just sent to your activity are now also sent to your Fragment.
For instance, things like onActivity result--
that's pretty useful, people use that, right--
things like permissions.
When Android came out and we added
runtime permissions, of course Fragments
got it, because Activity got it.
And also things like, On Multi-Window Mode changed.
Or On Picture In Picture Mode changed.
And these are all kind of things that we just
got kind of for free, being Fragments
and being this idea that there are micro activities.
But when you think about it, a lot of these things
aren't specific to Fragments.
It would be nice if anything could
get these kind of callbacks.
So we've kind of gone through this kind of existential crisis
on kind of moving away from this idea
that, it's like, oh, just because an Activity can do it,
a Fragment can do it.
So that kind of leads us to today.
That was 2011.
And we've tried to come a long way since then.
And a lot of this is really trying
to restructure what is our goal for Fragments?
And some of this is really just being a focused API service,
really making sure that it's just
the core piece that you need with predictable, sane
behavior.
That means no surprises, no things
that just randomly don't work.
But it also means we don't want to break existing consumers.
Right?
And that means binary source or even behavior compatibility.
Now along the way, that does mean that we do
have times when we introduce a new alternative, a new API,
that is predictable and nice.
And we deprecate the old API because we can't actually
remove it until we have a good alternative that
gives you something much better to work with.
But the idea is that we do want to release
a 2.0 at some point, where as long as you're not
using the deprecated APIs, if you're all on the good new API
surfaces, then it'll be an easy transition over
and we'll be able to cut out a lot of code out of Fragments
to support these kind of more legacy cases.
So what are we doing to get there?
Well, the first part of providing kind of a sane API
is a testable API.
If you're writing code that you can't test, that's no good.
And if we're writing a library that you can't test,
code-written with that library, that's
not going to work either.
So in 2019 here, we really want to make that better.
So we have a Fragment testing artifact now
that offers Fragment scenario.
This is really a way of testing just one Fragment in isolation.
And we worked really closely with the AndroidX test team.
So this is actually built on activity scenario, which
means it works for instrumentation tests
and it works for robo electric tests.
Now we wanted to make sure that this had kind
of a nice, small API service.
So really, it has one main method of on Fragment,
which takes a lambda, gives you the Fragment instance that's
available already.
And it also kind of gives you a lot
of the hooks that you need that makes it really
easy to test things lifecycle and recreating
your whole Fragment.
So what does this look like?
Well, it's actually pretty easy.
You create your scenario using Launch Fragment in Container,
which will do all of the creating, your activity,
adding the Fragment, moving it to resumed.
And then again just do a [? special ?] test, right?
You can say On View, click it.
And the Fragments hierarchy is already there.
It's already ready.
And then we can use on Fragment to then check
our internal state, check to see did it actually
handle the click correctly?
For example, if you're doing more complicated things,
if you want to check how, when you
move through lifecycle states, you can just call the scenario
Move to State and move it to Start It, or Create It,
or Resumed.
Same thing with actually testing recreation--
do I save and restore my state properly?
Just call Recreate.
You can check your state before Recreate.
You can check your state after Recreate.
And that's all you really need to do.
So the other bit is we have this recreation thing.
Well, that's really just one of many ways
that you need to instantiate a Fragment.
So we also have cases where you're adding
a new Fragment in first place.
You need to instantiate a Fragment
to give it to Fragment manager.
But also, we have things like Fragment Scenario.
You need to inflate the Fragment to add it
to your UI for testing.
So we wanted one way of doing this.
And this one way is FragmentFactory.
So this gives us kind of a way to now
finally be able to do constructor injection
into your Fragments and move away from this requirement
that you need a no-argument constructor.
Now you can actually build something
that does all of this for you.
So what does this look like?
Well, a simple FragmentFactory, it's really just one method,
instantiate, where you're given a class name.
And it's up to you to load the class.
So super dot instantiate will just use reflection,
call your no arg constructor.
But really, you can do anything you want with this.
If you watch the opinionated guide to dependency injection,
this is the kind of thing that we'd
love to do for you at some point so you
don't have to write this.
But even right now, if you just pass in arguments
into your factory, then you get a really easy way
of just passing those onto the Fragments that
care about them, using constructor injection here.
So then all you need to do in your activity is just
call FragmentFactory and set it equal to your Factory,
preferably before super.onCreate, because that's
when you're going to be reinstantiating Fragments.
Now, the other bit that we want to do
to kind of fix up all of this consistency everywhere is--
sorry, is all the other places that you
have Fragment creation.
So if you're doing a Commit and you're
adding a Fragment-- now instead of having
to do this whole instantiating and doing all that constructor
injection, you can just add it with a class name.
Here, we're using the [INAUDIBLE]
[? reified ?] version.
So now we'll delegate to your factory.
And now you have one code path for doing, instantiating, here
for the very first time when you're adding
or replacing a Fragment.
Similarly, when you're doing your Fragment scenario,
you just pass the factory.
It could be a mock factory that you've created just
to give mock dependencies.
Or it could be a real factory if you're
trying to do more integration test-style work.
So I'm going to pass it off to Jeremy
to talk about another area we're working on-- consistency.
JEREMY WOODS: Thanks.
So one [? instance ?] we found between adding a Fragment
to something like, say, a frame layout versus using
the Fragment tag, is it turns out
the Fragment tag adds Fragments using
a totally different system from Fragment transactions.
To provide consistent behavior, we
built a Fragment Container View as the one true container
for Fragments.
Fragment Container View extends frame layout.
But it only allows Fragment views.
This means if you're not a Fragment,
you probably shouldn't be using this.
It also can replace the Fragment tag
when you add the class or Android name attribute.
So in this example, because Fragment Container actually
does use a Fragment transaction under it,
you don't have to worry about--
you don't have any issues with replacing this Fragment later.
Fragment Container View also gave us the opportunity
to address some animation issues, specifically
an issue with z ordering of Fragments.
So here, we can see in the frame layout example,
the entering Fragment instantly pops onto the screen.
What's actually happening here is the entering animation
is sliding.
But it's underneath the exiting animation.
Fragment Container View ensures the z
ordering so we could actually see the sliding animation
for the entering Fragment, providing
predictable, same behavior across all API levels.
Another longstanding issue we had
is handling the system back from Fragment.
To address this, we added the OnBackPressedDispatcher.
Instead of adding a Fragment on the API,
now any component can handle a back via a new API
on the base activity class component activity.
Ideally, you'd inject the OnBackPressedDispatcher
for testing purposes.
But here, we're going to show you how to use it in code.
So a Fragment would grab a dispatcher
from its calling activity or from its activity.
And then we'll create a callback.
Here, we create that callback using the [INAUDIBLE]..
And we pass it this.
And we can pass the Fragment because a Fragment
is a lifecycle owner.
So for example, in your code, the user presses back.
And then we call Show Confirm Dialog when
the back button is pressed.
And then the user will say they want to exit anyway.
We'll disable the callback.
And then we'll use the dispatcher
to actually do the BackPressed.
So, no new Fragment API.
Instead, we just take advantage of the existing integration
between Fragments and architecture components.
So an architecture component is something
we want to leverage even more going forward.
So for example, it should be easy to get a View
model for your Fragment.
And here, we created the Kotlin Property Extensions
so you can get a view model at the Fragment level, the Nav
Graph level, or the Activity level
so your View model is always properly scoped
and you have the tools to provide that.
We're also leaning to the lifecycle as well.
For example, instead of using the custom lifecycle
map of Set User Visible Hint, you
can now just use regular lifecycle methods
when you're adding Fragments to a View page or one adaptor.
And so this is currently how View Page 2 works.
And only your current Fragment will be resumed.
Back to Ian.
IAN LAKE: Great.
Thanks, Jeremy.
That was a really good rundown of what's available right now.
So Fragment 1.1 had a lot of this.
Defining Container View is in Fragment 1.2, which
is in RC 1 as of yesterday.
But the other thing is I wanted to talk a little bit more
about where we're going kind of longer term.
There's a lot of other things that we want to do.
So I'm going to share a little bit
of a preview of some of the projects that we're working on.
But of course, because none of this is out yet,
this is all subject to change.
We're pretty happy with where this is all going.
But it's not something you can play with right today.
So the first thing is Multiple Back Stacks.
Some of you may have heard of this.
It's a thing.
But really, some of this comes down
to just that existing legacy on Android.
So on Android especially, at like the Activity level,
it was always a single stack of activities.
And Fragments just ended up inheriting kind
of that same structure where the only things that are saved
are the things that are on the Back Stack.
And we really want to move to a model
where we kind of support both, not just
the single stack approach but also
the multiple stack approach.
And really, that just means that we
allow you to save the state of Fragments that aren't actually
visible on the screen without losing their state.
So this is some of the cases that came up.
It was really like, Bottom Nav, things like Navigation Views.
And we have a sample app right now
that uses kind of a little bit of a work-around.
I say work-around-- it's some nasty Fragment code.
And really, we just want to make this really easy at kind
of the Fragment level.
So kind of the approach that we're taking
is kind of that same ability where each one of these tabs
can actually have its own stack.
So it has its own state.
So as you swap between these, it's
really more about saving and then restoring
a different stack.
And we're taking care of doing all the state
restoration and state saving as you go from stack to stack.
The other bit that we want to work around
is around returning results.
So we've heard a lot of information where you're like,
how do I talk between Fragments?
How do I talk between anything on Android?
It's always kind of--
there's lots of different ways of doing it,
some better than others.
And it turns out that the Fragment
APIs we have right now are not great.
So we have this whole set target Fragment
API that allows you to connect one Fragment with another,
and basically just keep a hard reference to another Fragment.
But it basically does nothing for the lifecycle
of what that other Fragment is.
So if that other Fragment is on the back stack,
it's actually stopped.
It's not started.
And if you actually talk to that Fragment
and it's trying to do like Fragment transactions
and stuff, that's going to fail because you're not started.
So we really don't have any kind of guarantees
around like, what state is that other Fragment actually
going to be in?
Additionally, we had like this idea
that, oh, you can use On Activity Result.
And I'm like, intense from Fragment to Fragment?
That feels kind of weird.
Like, we don't really need to be reusing some of these API
surfaces for callbacks.
So we're really kind of looking at this more holistically,
not just Fragment to Fragment, but also
things like start startActivityForResult.
Can we do kind of the same kind of API
surface for startActivityForResult,
for Fragment to Fragment communication,
from Navigation Destination to Navigation Destination?
Can we provide a common API that all these things can
[? time ?] to talk at so that when you're building callbacks,
it doesn't actually matter if it's
coming from a different activity or a different Fragment.
So that's kind of where we kind of see a lot of those APIs
going.
I'm really excited to share some of that in just a few months,
maybe.
The other thing is something we've constantly
heard feedback on Fragments is lifecycle.
Like, that's hard, right?
Well, it's like doubly hard with Fragments
because Fragments have two life cycles.
The Fragment itself has a lifecycle, which
is when the Fragment is actually attached to the Fragment
manager until it's removed from the Fragment manager and gets
destroyed.
But the Fragments view has a totally separate lifecycle
where when you actually put something on the back stack,
its View gets destroyed but the Fragment lives on.
So crazy idea, what if they were the same?
So we have this idea for what if, when the Fragment's view is
destroyed, the Fragment was destroyed at the same time.
And then when we want to recreate your View,
we recreate your Fragment.
And bringing these things together saves
so much complexity and means that things
like FragmentFactory and all the saving
state that you have to do anyways for cases
like, oh, I'm on configuration change, the process, death,
and coming back, Now, that just becomes the default option.
And now we only have to test one code path of like,
wait what state am I in?
Are my observers doing the wrong thing here
because these switching life cycles?
Now, I do realize this is a bigger change.
So we are looking at making this kind of an opt-in kind of API
at the Fragment activity kind of level.
So everything in that Fragment activity
would all move to this new world where
we have a much more simplified lifecycles case.
So we're really looking forward to releasing
all of these things.
And we'd really appreciate it if you
can talk to us in the Sandbox if you've got any feedback.
Also using the IssueTracker.Google.com
if you have any feedback or other feature requests
that you'd love to see in Fragments.
Thank you.
[APPLAUSE]
[MUSIC PLAYING]