Breeze deep links with Android Architecture Components Navigation
While developing Android apps you can face this exact task “Integrate Deep Linking support” and it’ll be your nightmare because of several reasons. And those reasons are:
- Add all links to manifest file (also should consider
http
orhttps
) - Handle link parameters (it could be GET Query or path parameters)
- Handle user navigation after opening app from deep link
Each of those reasons can be painful one by one, but all together it’s straight hell to handle.
How to handle either http, or https?
It’s simple, you just declare two filters. Isn’t it simple or obvious?…
How to extract parameters
You just parse them into Uri
and then do your business. But even if it’s quite simple for query parameter :
val myUri = Uri.parse(myLink) // http://mysite.com?myParam=VALUE
val myParamValue = myUri.getQueryParameter("myParam")
It’s not as simple for path parameters:
val myUri = Uri.parse(myLink) // http://mysite.com/VALUE/stuff
val myParamValue = myUri.pathSegments[0]
Here you need to know exactly what and where it’ll be in order to access it
How to handle navigation
Here’s most painful part. How do you handle if user press back/up? Imagine situation where you have links like this http://mysite.com/posts/POST_ID/comments
and your UI flow like following Posts -> Post -> Comments
, so you need to return user to post screen and then on another back press you need to show posts screen. How can you achieve this?
- You can set up global deep links handler that will not only start comments screen, but also fill stack with other screens (posts and post in this case). Something like this:
val stackBuilder = TaskStackBuilder.create(this)
stackBuilder.addParentStack(MainActivity.class)
stackBuilder.addNextIntent(postIntent)
stackBuilder.addNextIntent(commentsIntent)
stackBuilder.startActivities()
2. You can handle this situation directly when comments screen is about to close
fun closeScreen() {
if (isTaskRoot) {
// do same as above but without comments
}
finish()
}
AAC Navigation to rescue
When I read release notes for AAC Navigation I was like “wow, it is it?” . Yes, they had full chapter to describe Deep Link solution.
Briefly about AAC Navigation
Android Architecture Components Navigation it’s Google’s solution to navigation handling in Android. With it you describe your navigation destinations and required actions (move from one destination to another) in XML file and then you’r just calling Navigator.navigate(id)
to go to some destination.
And also worth considering that it can be done and pretty visualized in visual editor window.
You can read more about Android Navigation Architecture Component in Jovche Mitrejchevski article:
Deep stuff
And finally why are we all here, the new way to handle Deep Links by Google looks like this
<fragment
android:id="@+id/pagesListFragment"
android:name="com.ekalips.navigatortest.pages.PagesListFragment"
android:label="Pages"
tools:layout="@layout/fragment_pages_list">
<action
android:id="@+id/viewPage"
app:destination="@id/pageFragment"/>
<deepLink app:uri="myapp.com" />
</fragment>
Yep, that small fragment with deepLink
tag handles all for you. You can see, that it doesn’t contain http
or https
because as written in documentation “URIs without a scheme are assumed as http and https”. Hooray, it’s solved first problem.
But wait, what with another two problems? Let’s go further.
Here I have declared another destination:
<fragment
android:id="@+id/pageFragment"
android:name="com.ekalips.navigatortest.page.PageFragment"
android:label="Page"
tools:layout="@layout/fragment_page">
<argument
android:name="title"
app:type="string" />
<action
android:id="@+id/viewPageComments"
app:destination="@id/pageCommentsFragment"/>
<deepLink app:uri="myapp.com/{title}" />
</fragment>
This fragment requires “title” parameter in order to start. Take a look at deepLink
tag now. It has some weird {title}
thingy. And yes, if link has some parameter name in curly brackets and parameter matches one of destination arguments — i’ll be parsed and handled automatically. So you can do your navigation either with bundles or with deep links, all params will be handled identically.
When you are done declaring deep urls and destinations, you should only add one line to your manifest activity declaration to tie app all together
<nav-graph android:value="@navigation/your_navigator" />
(Note that <nav-graph>
will only work on Android Studio 3.2+, in older versions you still need to add all by hand :) )
But there are few rotten apples that spoil the bunch
- That automatic url parameters handling has some issues with GET Query parameters (in my example all urls with this pattern
myapp.com/yet/some/another/path?title={title}
always led me to page that handled justmyapp.com
). I really hope that it’s a bug that will be fixed in future - Back navigation is still a problem. For some reason back navigation still doesn’t work in my example, although docs clearly say that back navigation will be handled like user came to this page by himself. And also it even behaves differently when I press Up on AppBar and when I press Back on phone’s NavBar. So it’s clearly something broken here.
Conclusion
I think that Google came up with really great tool for handling navigation in Android. And even if I’m not quite big fan of single-acitivty application, I can clearly see reasons for this solution (there are plenty of answers in the internet why Google made a bet on single-activity applications).
You can checkout my example on GitHub:
Feel free to read more about Deep Links handling in AAC Navigation in official docs.
UPDATE: 13th October 2018
Google just responded to me on the issue tracker that Deep Links with GET query parameters will be fixed in the next release of navigation library. You can read full thread here.