Android Jetpack: manage UI navigation with Navigation Controller (Google I/O ’18)
Articles Blog

Android Jetpack: manage UI navigation with Navigation Controller (Google I/O ’18)

August 8, 2019


[MUSIC PLAYING] LUKAS BERGSTROM: Good morning. Thanks for getting up early
to be here with us even despite the fact that
some of our thunder might have been stolen by the
three or four presentations before this that demoed
the Navigation Editor. Luckily, we have time to go
into a little bit more detail than any of the
previous presentations. So there’s a lot of
good material here. And I’m Lukas, Product Manager
for Architecture Components, and with me, I have
Ian and Sergey, who built the navigation component. And navigation, if
you think about it, is a problem that pretty
much every app on Android has to solve. But until now, we
haven’t really given you anything to do that with other
than Start Activity, which, for various reasons, is not
the best way to go about that. So if you think about
our job of making Android development
easier, navigation was a common problem
with no real solution. And that means that there
are a lot of things that you have to solve on your own. And those ranged from how to
commit fragment transactions, hopefully without
throwing an exception, how to test that navigation is
happening correctly and that the right things are happening
when navigation occurs, how to map deep links to
various places in your app, and how to keep those deep link
schemes up-to-date as your app navigational structure changes,
passing arguments from place to place– again, we don’t give you a type
safe way to do that today– how to make sure that Up and
Back take users to the right places, particularly in
more difficult situations like someone deep linking deep
into your app’s navigation hierarchy. And so what this means
is by the time you finish solving these problems,
you’ve typically gone one of two directions. You’ve either written 60%
of a navigation framework or you’ve got a lot of
error-prone boilerplate everywhere navigation needs
to happen in your app. So you’ve got a bunch of
parallel lines of code solving these various problems,
and the whole structure is fairly brittle. And individually, these
problems are pretty tractable, but if you look at a
real world example, you can see that they
can get pretty hairy. So say that I have an
item screen in my app, and it’s accessible
via deep link. So far so good. But if someone had navigated
to the screen via opening the app from the
home screen, they would have a couple other
screens on the back stack. And so hitting it
up, we want them to take that not out of the
app from the item screen. We want them to go to the
category screen and then the home screen. And that means that if someone
deep links into the app, we need to synthesize
these screens and add them to the up stack
before showing the item screen. And talking to a
third-party developer, he told me it’s when you’re in
the middle of writing the code to do this, to
synthesize these screens and add them to the
up and back stack but only on a deep link,
that’s when you start to feel like maybe
you’re solving a failure of the framework. So it’s to help solve
problems like that that we’re launching Navigation. And what we’re giving
you is a visual tool that lets you edit the
navigation graph of your app, which is represented in XML. And that lets you define a
set of available navigation actions, arguments you can
pass from place to place, things like visual transitions. And then a single
Navigate call activates all of that at runtime. And so last but not least,
that means one thing you never have to worry about again is
touching a fragment transaction with your bare hands. [APPLAUSE] So what the navigation
graph is essentially just a set of what are
the possible destinations people can reach in my app. And those usually correspond
to screens, but not always. So you can have the
navigation control or just changing the contents
of a smaller part of the screen, but it’s a set of
navigation destinations and the actions that link them. And the actions really
represent how you can get from place to place. So to actually navigate from
point A to point B in an app, you’re just going to call
the correct navigation action at runtime. So let’s take a look at
what this looks like. And let’s switch
over to the demo. OK. So what we see here is a set
of navigation destinations, and these are
fragment destinations, although other
options are possible. And the lines connecting
them with the arrowheads are actions. And those actions
actually generate methods that you can call at runtime. This whole thing is backed by
XML, as you all know and love. And the XML and the
Navigation Editor have the same set
of capabilities, so you can use either one. We’re going to
add a screen here. And what you’re seeing is a
set of available activities and fragments in my app. OK, so we just added
an option for them to actually answer this question
successfully and win the game. So now we’re going
to add an action to navigate to that screen. Great. And so that
navigation action has a bunch of options that
you can set, obviously. We’re going to talk about
those a little bit more. For now, we’re going
to do one thing. So we’re going to
say, if they’ve gotten to this
Congratulations screen, that means the game is over. So hitting Back
shouldn’t take them back into a game that
no longer exists. So what that means is we
want to, on that action, say, let’s pop to
the Match screen. And that means I’m going
to pop off everything on the back stack in between
this destination and the match screen. So when the user gets to
the Congratulations screen, when they hit Back, they’re
just going to go straight to the Match screen. So a lot of other
options I can set, but we’ll just talk
about that for now. Let’s go back and look at the
Congratulations screen again. One other thing to mention,
the key thing that is set here, is the fragment class. And that’s what’s actually
instantiated at runtime. And we see layout previews here
because the Navigation Editor knows what layout is associated
with that navigation graph. And because it knows
what layout is associated with that navigation
graph, I can double click on that
fragment destination to get here into
the Layout Editor. Great. And everything that
I’ve just done here, adding this new destination
to the navigation graph, an action, changing
what that pop behavior of that action is, all
this stuff I can also do programmatically at runtime. So Navigation Editor,
XML or programmatic, all work just fine. Great. And now, I’m going to
hand this off to Ian to walk you through
some more detail. IAN LAKE: So the first question
that might come to mind is, wow, this is
a totally new way of structuring the
UI of your app. And kind of brings up
this question immediately like, OK, so what is my
activity actually meant to do. Right? So every kind of app has a
very different starting point from where you guys
are right now– or maybe as of two days ago– on how you’ve structured
the UI of your app. Some may be very activity-heavy,
very fragment-heavy, very much in a different system. All those are certainly
very valid places to be, but we’re moving towards a model
where the activity is more just an entry point into your app. Rather than the
activity being the owner of the content of your
app, it’s actually just what’s going to store
that global state. So the global
navigations, so if you have bottom nav,
navigation drawers, you’re still using
an action bar, those are the kind of things
that Activities manage, but it delegates to what we
call a NavHost for content. So our nice super
simple activity here. We have an action
bar on the top, a bottom nav on the bottom,
and this big white box in the middle. That’s our NavHost. So in the world of
navigation, when you navigate between
different destinations in your app, what
you’re actually doing is just replacing
everything inside that box. Right? So you have that global
navigation outside of that, and we have hooks that
you can hook that up to stay in sync with
the actual NavHost. So what is a super simple
version of this look like? Well, if you’re using
fragments for destinations, you would want to use
our NavHost fragment. So here, we just have
a activity layout that is literally
just our one fragment. So you do this by including the
navigation fragment dependency as you might expect. If you’re using a totally
different kind of destination, it would probably also have
a different kind of NavHost that you add here, whether
it’s a custom view or whatever other item that they need. But for NavHost fragment,
we’ve set up a few convenience methods. The ability to set
what navigation graph you’re using
in XML, so you don’t need to do anything
programmatically to set it up. It’ll just go to the start
destination of your graph by default. And then for fragments,
we can actually hook this up to the
system Back button. So we offer a method
to do exactly that. So you don’t have to override
on Back pressed specifically for navigation. We’ll hook up all that stuff
using the magic of fragments through this default
NavHost option. So that means that
our activity actually gets to be two lines of code. It would be two lines, but
it doesn’t fit horizontally on a slide. But all we need to
do is just inflate our layouts, that content view,
and then hook up the Up button. Now normally, this
is a large operation, but because you’ve told us about
the structure of your graph, what we can do
is– in this case, we’re using a Kotlin
extension method on activity. It’ll just be a static
method for Java users that allows us to
find the navigation controller by passing
in the ID, in this case, the ID of our NavHost fragment. And that just gives us
access to navigate up. And navigate up is going
to do the right thing based on your navigation graph. You don’t need to actually
do a lot of extra work here. By giving us your graph, we
are able to do this for you. But for most apps, just
having a single thing is not actually what you have. Maybe you have something
a little bit more. So we’ve kind of set up a second
dependency, navigation-ui, which is really just a
set of static methods that connect your navigation
component with some of the material
design components, some of these things like
bottom nav and things like that that are very much in
that kind of global navigation space. But of course, it’s
2018 so we have KTX, one that changes those
static methods into extension methods on the classes
that they operate on. So this is really
easy for Kotlin users to integrate into your
app and have navigation feel like it’s
just something that exists in all of the
components that are out there. So what does this look like? If we, again, make our activity
a little bit more complicated, we’ll add a tool bar
on the top, and we’ll add a bottom navigation view. And in this case, we still
have the same kind of app menu that you had before on a
bottom navigation view. But what we do to hook those
things up, it takes two parts. One, your menu here
is actually going to be using the same IDs that
you’ve had on each destination. So each destination
has a unique ID. And we can actually use those
same IDs on your menu items. So that kind of builds
this implicit link of, oh, well if you click
the Home menu item, you’re going to go to the
home destination of your app. So in code, we’re just going
to set up our action bar using our toolbar. And then we can do the
same findNavController to get our access to the
NavController object. And then we just have a call
and extension for activity that allows you to say, set up
action bar with NavController. And this does quite
a bit of magic. But what it’s doing
is it’s actually going to be receiving events
on when you’ve navigated in your NavController and using
the labels that you’ve set up in your navigation graph to
update the title of your action bar. And we also have
another helper method if you’re using a drawer layout
to automatically change it from a hamburger button
into a back arrow, based on what
destination you’re on. So really just those
helpful patterns to make sure that those
things stay in sync. Similarly, for the bottom
nav, you just call setup. You call setup
with NavController and redo the two-way
syncing here. So as you click on
things in the bottom nav, it’ll change the graph and
do the correct transition based on the
material guidelines, as well as as you
navigate between your app, if you have separate
buttons, it’ll actually update the selected
item in the bottom navigation. So this gives us a lot of
power, but not everyone is just using
pre-built components that another team has provided. You have all your own custom UI. So at that point,
we really need to go deeper into what NavController
actually gives you. And for the super simple
case, you have a button. You want it to go somewhere. We have a convenience
method, create navigate OnClickListener. You give it the ID of
where you want to go, what destination, what
action you want to trigger, and we’ll do all
the work for you. Now, this is, perhaps, a
little bit too magical. So you can unroll it
just a little bit. In this case, we’re using
another extension method on view. So from any view that’s been
created by NavController, you can actually get a reference
to your nav controller, just by calling
findNavController, as you might expect, and
use that nav controller to call Navigate. And just navigate to
an ID of a destination or an action in your graph. That’s it. Under the covers, this
Navigate is actually doing a lot of work. So the NavController is talking
to, what we call, a navigator. And so for fragments, we were
talking to a fragment navigator that we provide. And that navigator knows
everything about, oh, you called Navigate. I know what fragment you’re
going to because you gave us your class name. And it’s going to build all
of the fragment transaction for you. It’s going to call
Add to Back Stack. It’s going to do all
the things that you told us to do by putting that
information in your navigation graph. So if you had Pop
Up To, it’s going to do all that sort of stuff. Transitions, all of that,
is in this one line of code. And all of it can be determined
either programmatically– we can add additional
options to this method– or something that you
determine ahead of time as part of your
navigation graph. But for a lot of
these places, it’s not actually just a Navigate. Right? You have some information
to pass to the next source. So for this, you need to
pass a bundle of information. So here, we’re passing
a string and an int, and we’re using our nice helpful
bundle of from Android KTX. And it works. It’s fine. This is really useful
for a lot of things. But at the same point,
it’s not very safe. If you do a mistype here,
what are you going to do? So we really want to
make this a lot easier. Sergey’s going to talk
about what we did here. SERGEY VASILINETC:
Yeah, we built something called Safe
Args Gradle Plugin and it will help
you a little bit. But first of all, let’s see
what we are trying to resolve. And let’s go back
to our example. Our help fragment where we
try to navigate actually requires us to pass
screen name argument. And optionally, you
can pass category which has integer type. And let’s go back
to the calling site. Well, in our slice, we
made everything correctly. We passed screen, and
it has proper type. We passed category. It has proper type as well. But actually, during [INAUDIBLE]
some other [INAUDIBLE],, you can forget to
pass screen name. It will result in
runtime exception. And don’t me wrong,
it’s super easy to fix. But it feels annoying. It feels like, what am I,
a Java Script developer? Why does this
compile time check? Like, I don’t know. So yeah, we decide, OK, now
we have this navigation graph. Let’s put all about
navigation there, including the arguments
of your destinations. So let’s see how it
looks like in XML. And it’s super simple. We just specify our argument
name, s-type, additionally as specified default value. It means that your
argument is optional. And this allows us
to build tooling that once you have
an action that leads to this fragment or
other action or activities, we can check and make you
pass proper arguments. So let’s take a
look how it looks like with our Gradle plugin. Now, to navigate, we
use this special object, which instead of passing ID in
a bundle, of course internally the subject incorporates the
same ID and same arguments. But to get that object we use
HomeFragmentDirection class. It’s generated for you. It suggests a fragment name
plus suffix Directions. And this class
has static methods for every action defined
for this destination. And those static methods make
it pass required arguments. So in our case, it makes us
to pass home argument there. And yeah, optionally
can later set up your other additional arguments. And everything is type safe. And receiving side after
that is super simple. You have this [INAUDIBLE]
that they generate for you. It’s args class. So for our case, it’s
HelpfragmentArgs. And it just has all
of your arguments that you defined in
a typeset manner. And from this relatively
small life improvement, we go to the bigger one,
which is deep links. IAN LAKE: Yes. So deep links are
traditionally something that Android has supported
for the longest time. So you can add intent
filters to activities and take over a web URL,
as well as deep linking is super useful for
notifications and things like that to link
back into your app. But this gets a lot
more complicated as you get a more
complicated app. Like how you structure these
things and how you say, all right, I need to
build a notification. What is all of the code
that’s needed to actually get into the correct place in my
app and pass the right kind of information here? So for navigation,
we really made deep linking kind of
a first-class citizen in our structure. So there’s really two kinds of
deep links, the explicit kind– so these are things like for
notifications, app shortcuts, app widgets, and the
new actions and slices, things that are things that
you create and are usually pending intent based. These are all things that
you’re passing to another app or to the system to say, I want
to go to this specific place in my app. The implicit side of things
are more around the web URLs and custom scheme URLS. So these would be other apps
triggering your app to launch. And we handle both of
these for Navigation. For explicit deep links,
we have a specific class called NavDeepLinkBuilder. And it’s sole goal in
life is to deep link to a specific destination
in your navigation graph by its ID. But it’s going to
do all of that work. It’s easy to say, but
it’s a little bit harder to actually make sure that
all works in the system. But if we create a
NavDeepLinkBuilder, you create it with
context, you give it your graph, your destination,
any arguments you have. And then you can just call
Create Pending Intent. And we’re doing all
of the work here to create the right
synthetic back stack, both within your
graph and if you’re using multiple activities,
your parent activities as well. And we’re going to pass
that along and create the correct intent that gives
you to the right place when you trigger this intent. And then you just pass it
through to your notification. You don’t actually need to
do more than this to get all the correct behavior. For implicit deep links– these are, again,
links to web URLs. Right? In this case, instead
of it being something that you create
programmatically, it’s information you include
in your navigation graph. So here, we’re adding
a deep link element. Just like we added the arguments
and actions to our graph, these are just a deep link. And of course, you could do
all of us in the Visual Editor as part of the properties
for a destination. And it’s really just as
simple as an app:uri. And you pass in a uri. Now, this is a static uri,
which is boring and dumb. And there’s only so many apps
that have just one uri they do. So we, of course,
support some wild cards. So if you want to do a .* for
a wild card, totally supported. If you want to fill in the
arguments for your destination, you can actually
use curly braces. And we’ll parse the URL for
you and extract those values and give them to
you for your args. So again, kind of
in that same type safe args, now you can get
those directly from URL and not have to reparse things. You already know what
this is supposed to be. Similarly, you can
combine the two. If you want to make more
complicated patterns, totally can. We also have support
for auto verify if you’re using app links
to skip that disambiguation screen. We wanted to make sure that you
could do the same kind of thing if you’re using
Navigation as well. And you’ll note here that
we left off the HTTP, HTTPS. So what we’re doing here
is actually doing both. We’re saying HTTP, HTTPS. Now, I assume your
servers are all HTTPS. But you can’t really
control the URLs that other apps are
including throughout. Maybe they accidentally
took the S off of your URL. We still want to
support both of those. So we use this just as
a convenience method instead of having two
lines instead of one. And of course, it also
works with custom schemes. So if you have your own scheme
that you’ve set up specifically for your app, you can also
attach those to deep links. Now the best part
is we’ve kind of worked across the tool space. So besides just the
Navigation Editor, we’ve also worked with
the manifest merger team. So you can add just a
single nav graph element to an activity in your manifest
pointing to your graph, and all of the deep
links in that graph will then get expanded out to
be the correct intent filter. We’ll build all those for you. And if you go to the Manifest
Merger view in Android Studio, you actually see the exact line
from your navigation file that generated that intent filter. So this means that we now
have a single source of truth in your navigation
graph that you know this is not
going to get out of sync with what you expect. It’s not going to get out of
sync as you change argument names in your XML file. All of this is one central
place to do things, and we think this
is a lot easier for basically all of
the implicit deep link kind of cases. Of course, we do do things where
this is all ActionView URLs as they would be for web URLs. So you can see it’s added
directly to the line. And if you have
multiple of them, it’ll actually tell
you exactly what line. If you have multiple
graphs associated with different activities,
those will all work just fine. So one of the
other subjects that is really important to all
of architecture components is testing. And testing of
navigation is very hard. And this is something
that we’re going to continue to look at
over the alpha period. And we really want all
of your feedback as well. But I wanted to discuss what we
think testing in a navigation world should look like. So a lot of it is if all of the
links between your destinations are through navigation, then
it’s a lot easier to test a destination in isolation. Right? You can test each
destination by itself and then test just the
outgoing edges or just the incoming arguments and
not have to deal with, oh, did it actually do u right
fragment transaction? Because we can test that just
at the Navigation Controller level. So this is something
that we are going to spend a lot more time on. And in the fragment talk
yesterday, we actually talked about really trying
to make fragments themselves much more testable in isolation. So it’s kind of a
package deal where we are trying to build testing
into Navigation Controller but then also trying to build
testable destinations as well. So you might be interested
to do something right now. So if you ran an
Espresso test and you want to test, oh, when
I navigate to something, does it go to the right place. Well, we actually have an add-on
Navigator navigated listener. So you can actually hook it
up to the NavController’s navigator and get
a callback of, oh, did you go to the right place
when I click this button. So this is one method that
we found pretty successful in testing things completely
black box outside of things. Obviously, if you want
to inject a NavController or use any other method, those
are also perfectly valid ways of setting things up. So what can you play with today? And what could you play
with– you had two days. You must have looked at it. Right? So it is in alpha right
now, 1.0.0-alpha01. We’re giving ourselves a
long runway of bug fixes and improvements here. And it really comes down to two
main artifact, the navigation fragment, which includes the
navigation runtime dependency as a transitive
dependency and also the NavHost fragment and
the fragment navigator that you need to use
fragment destinations. As well as the
navigation UI dependency, which has those static methods. And for every one of the
dependencies for navigation, we have a dash KTX version of
them if you’re using Kotlin. So we really tried
to make Kotlin a first-class citizen
in the navigation world, especially for
some of the things like if you are doing
programmatic graph construction. Like say you’re reading
your whole navigation graph from a server,
we have a Kotlin DSL as part of our Kotlin extensions
to make that a lot easier if you’re using Kotlin. So there is more to do,
and I’ll have Lukas talk about where we’re going. LUKAS BERGSTROM: Yeah. One note, you are going to
need Android Studio 3.2 Preview Canary 14 to use this. And please do download
it and try it out. There’s a lot of
great stuff there. So this is obviously
going to become a really core part of not
just architecture components but JetPack overall. So with JetPack, we’re going to
take the same approach we took with architecture components. Like a sort of a
blank sheet of paper, what do we want the Android
developer experience to be? And apply that
much more broadly. And navigation will
be eventually part of the default experience. So when we get to stable,
then creating a new project in Android Studio is
going to, by default, start you up in the
Nav Editor, and it should be a pretty great world. And JetPack is available
for you to try right now. Right now, it’s sort of got
a much nicer introduction to what all the key pieces
of Android development are. So it’s a much easier on ramp. And we’re looking forward to
expanding the story over time. There are more talks for you
to go to, a lot more detail on Android KTX and paging. Paging, in particular, is
a really cool deep library that does a lot for
you by tying together different pieces of architecture
components in JetPack. So I encourage you,
if you ever have a list view with
more stuff than you can hold in memory
at any given time, you really want to go
to this paging talk. And we want your feedback. We want your feedback on
this session, first of all. But more importantly,
we want your feedback on the navigation component. The reason that we’re
launching to alpha is not because we
think that this is really untested and untried. We’ve actually done a lot of
pre-release testing of this. But we were
launching it to alpha because we want to
get a lot of feedback from the community on it
before we locked down the API and switch over to beta. So this is a great time
for you to try it out. Tell us what works for you. Tell us what doesn’t, and either
communicate with us directly or on our public issue tracker. We would love to hear from you. Your feedback has
been really critical in every point of this
journey in making sure that we’re attacking
the right problems with the right solutions. So please do try it out,
and tell us what you think. And thank you. [APPLAUSE] [MUSIC PLAYING]

Only registered users can comment.

  1. I waited so much time for something like this. It's a big improvement for the Android developers life! Thank you!

  2. How do I add business logic to the navigation? You showed static navigation, what about dynamic?

  3. Now the iOS developers that work near me can't mock me anymore for not having a storyboard 😀

  4. How do I handle CollapsingToolbarLayout for specific Fragment if I have Toolbar that is placed in the Activity?
    Do I hide the Activity's Toolbar and then handle Fragment's Toolbar separately? How will this affect the up navigation?

  5. Will there be a
    implementation 'android.arch.navigation:navigation-activity:1.0.0-alpha01'
    that fixes the up and back button handling for activities without the need to switch to this Fragment style?

  6. Okay so this explained some parts. But what about master-detail flows on tablets? How do I tell it whether I'm on a phone or tablet ui and where to load the fragment to? How would I modify the ActionBar Menu? What about Activities deeper in the hirarchy where I want an Up-arrow even on the topmost fragment to go up to another activity? So many things that are currently not explained anywhere, so we'll need to wait for blog posts and stackoverflow questions to pop up to fill all the blanks. Just like every io unfortunately. Things will be usable around the P release.

  7. Can a fragment in the navigation graph have multiple pop-to fragments? Can a navigation graph looks like a net?
    For example in the case of 06:17, what if an app has multiple entries to the user_profile? Or I'll need to create multiple separate paths?

  8. With larger applications with many fragments – can you have multiple .xml files for the navigation graph or can you only have one very large graph for the entire project?

  9. Finally after 10 years they are starting to understand that Android Fragments and UI design are completely shitty… And then they take inspiration from Apple ? Really ? What is going wrong with you Google ?

  10. why they copy ios i dont understand and even if they copy they cam make it better but these are only good for fragment transactions Not activity result

  11. How to manage fragment state duting backstack. Like first contains recyclerview and user scroll to end then onitemclick navigate to second screen. now if user back to previous fragment there is not state managed. it is possible or not.if possible how to manage ?

  12. Hello from 2019, navigation editor not working properly, as always all new features not production-ready – data binding, navigation… Looks like experiment, but not as usable feature.

  13. The details are so satisfying and awesome !! 🙂

    Oh by the way, is it ok if I can also seek your advice in this open source android app I have posted below? Just need some feedback about it…

    http ://bit. ly/2tldxaG

    thanks a-hundred-times !!

  14. Nice! I will be happy to follow and learn from you. 😛

    Oh by the way, is it ok if I can also seek your advice in this open source android app I have posted below? Just need some feedback about it…

    Just need to search ' pub:Path Ahead ' in Google Play (P & A are case sensitive).

    thanks a-hundred !!

  15. Every back button or navigation item selection recreates the whole fragment. So what is the use of this if I can't keep the last state of the fragment state.

Leave a Reply

Your email address will not be published. Required fields are marked *