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]