- Sharing Data using SafeArgs in Android-Kotlin
- Introduction
- Prerequisites
- Table of contents
- Create an Android project
- Enable viewBinding
- Create two Fragments
- Starter code setup
- i). FragmentA.kt
- ii). fragment_a.xml
- iii). FragmentB.kt
- iv). fragment_b.mxl
- Creating the navigation graph
- Add destinations in the Nav-graph
- Create the host for our Navigation graph
- activity_main.xml
- MainActivity.kt
- Set up SafeArgs
- What argument types does SafeArgs support?
- How can you define an argument?
- Passing values to the argument
- Receive the argument at the destination
- Sharing custom arguments
- Creating a custom object Argument
- Modify share action in FragmentA.kt
- Receive the parcel in FragmentB.kt
- Conclusion
- About the author
- Want to learn more about the EngEd Program?
- Safe Args — верный помощник Navigation Component
- Show me the code!
- В итоге
- Using Safe Args With the Android Navigation Component
- Version
- Getting Started
- Why Safe Args?
- Adding Safe Args to Your App
- Passing Data With Safe Args
- Defining an Argument in a Destination
- Adding a Custom Type Argument
- Retrieving Arguments in a Destination
- Defining an Argument in an Action
- Sending Arguments From a Destination
- Safe Args and Proguard
- Where to Go From Here?
Sharing Data using SafeArgs in Android-Kotlin
June 25, 2021
Data sharing in Android involves passing arguments between fragments in navigation action. This not only enhances communication between the destinations involved but also establishes a continuous flow of the application.
Introduction
In the past few years, Android developers have made use of the Android Bundle class which was one of the techniques of sharing data across activities.
This came with a bunch of cons among which is the tedious work the developer had to do, the unreliability due to the manual approach, and lack of type safety that could easily crash the application.
The Navigation components API, (part of Jetpack libraries) is a MAD (Modern Android Development) approach that solves these problems by introducing SafeArgs — a plugin that allows you to pass data in a more efficient, safe, and encapsulated way.
Prerequisites
To follow through this tutorial, you need to be familiar with:
- Basic usage of Android studio.
- Kotlin programming.
- Imperative paradigm concepts in Android development.
- View binding and/or data binding.
Table of contents
In this tutorial, we’re going to:
Create an Android project
Fire up Android Studio and create an Empty Activity project with the following configurations.
Double-check to make sure that the package name is as shown. Otherwise you’ll have to configure your project to make it compatible with the code used in this tutorial.
Enable viewBinding
View binding allows us to access views in the XML file via the respective binding class and the view’s id. Open the app-level build.gradle file and paste the following inside the android scope and sync the project.
You can learn more about view binding here.
Create two Fragments
Moving on, we need at least two fragments that we’ll use to pass arguments across when navigating. Right-click on the project’s main package directory and create two empty fragments namely FragmentA and FragmentB .
Let’s nickname them A and B to keep things simple. The two should have their corresponding XML files namely fragment_a.xml and fragment_b.xml respectively.
Starter code setup
The following is the initial code that we’ll build on.
i). FragmentA.kt
Here, we’ve inflated the fragment using view binding.
ii). fragment_a.xml
This serves as the UI for FragmentA. The above code creates a button that will later be used to trigger a navigation action when clicked.
iii). FragmentB.kt
This serves a similar purpose as that of FragmentA.
iv). fragment_b.mxl
The above textView will be used to display data after a successful arrival discussed in the latter part of the tutorial рџЋ.
Creating the navigation graph
A navigation graph popularly known as nav-graph controls and visualizes how we maneuver between the fragments. To create a nav-graph, switch to Resource Management on the left side panel and select navigation .
Click the + button and create a new graph named my_nav . This will prompt you to automatically add the respective dependencies.
Click OK and you’ll be good to go.
Alternatively, you can manually add the following dependencies in the app-level build.gradle file.
Add destinations in the Nav-graph
Destinations are the screens or rather fragments included in a particular navigation graph. Go ahead and paste the following code in the my_nav.xml file we just created.
This adds two destinations, sets fragmentA as the initial fragment, and creates a navigation action from A to B.
Create the host for our Navigation graph
Now that we’ve created a graph, it’s time to set the activity that will serve as the parent and the entry point for our app. This involves adding a nav host fragment in the activity’s XML file as shown below.
activity_main.xml
NOTE: Android Studio will complain and suggest that you should use a FragmentContainerView instead of a fragment tag. I found out that addressing the warning results in weird unexpected crashes discussed in this Google issue tracker.
MainActivity.kt
This inflates the UI with nav-graph and updates the toolbar with respect to the current destination.
Set up SafeArgs
Now we’re ready to take a flight with safe args, but before then, let’s first pack our bags.
This involves loading the required dependencies and plugins. Add the following classpath in the project-level build.gradle file.
In your module-level build.gradle file, add the following plugin.
Check if you have Java-8 support enabled as most Gradle plugins (including safe-args) require JDK 8.
It is recommended to fully rebuild and clean your project after syncing to make sure that navigation component tools are generated.
What argument types does SafeArgs support?
The data attached to a navigation operation is referred to as an argument. Arguments can take different types but not all types are supported by SafeArgs.
The following is a list of supported data types.
- Custom Parcelable
- Custom Serializable
- Custom Enum
- Resource Reference
How can you define an argument?
To add an argument to a nav-action, select the destination fragment in the navigation graph preview and click + on the attributes panel. A dialog will pop up as shown below.
This can be used to define attributes such as name , type , nullability , and the default value where applicable. In the example above, we’ve created myAge which is an Integer whose default value is 1.
By clicking Add , the following tag is auto-added.
Passing values to the argument
Think of a scenario where you need to order a pizza to be delivered to you. You’ll have to place an order before delivery. Similarly, when we want to pass data from A to B , B must first need the data, that is, there must be an argument in B and a path connecting A to B . This path is the navigation action that we’ve already created.
Moving on, declare an action variable and assign 19 to it for example. Remember the value must align with the type of the argument. In this case, we can only pass integers, otherwise, a type mismatch exception will be thrown.
Paste the following in the onCreate() method in FragmentA.kt file.
Receive the argument at the destination
Now, our pizza delivery is in progress. We need to prepare to receive it upon arrival.
Head to FragmentB.kt and update the code to:
Here we’ve declared a variable args that takes the argument(s) associated with FragmentB. We’ve also set the value of the textView to the value contained in args .
Similarly, we can share other data types as well.
Sharing custom arguments
Other than predefined data types, SafeArgs allows us to pass objects of our desired type. To demonstrate this, we’re going to pass a person object using a custom Parcelable argument.
This way we can share different data types under the hood of one type. Add the following plugin to add Parcelize to your project.
This plugin provides a Parcelable implementation generator that automatically generates parcels for classes annotated with @Parcelize annotation. Such classes must extend Parcelable which is an Android specific interface where we serialize objects ourselves.
Create a data class Person that implements the above information.
An object of the class above will have two attributes, name and age.
Creating a custom object Argument
A parcelable argument is created the same way as predefined types only that we select the Parcelable class as its type.
Alternatively, we can add the argument tag below.
Modify share action in FragmentA.kt
First, we need to instantiate the Person class then pass it in the action’s parameter.
Receive the parcel in FragmentB.kt
Similar to what we did before, we’ll receive and display the value of the argument in a textView.
Conclusion
In this tutorial, we’ve learned how SafeArgs can be used to pass or share data across destinations. Note that it is not recommended to pass large amounts of data since argument size is limited in Android.
In such a case, I’d recommend you to use a ViewModel as discussed in this blog. The source code for this project can be found on my GitHub.
Peer Review Contributions by: Peter Kayere
About the author
Eric Gacoki is a 2nd-year undergraduate student pursuing computer science. He is a self-taught and solution-driven Android software developer. Currently, he’s doubling up as a co-lead in Android Stack under Developer Students’ clubs (sponsored by Google) in his university. He enjoys teaming up with all levels of developers to solve complex problems and learning from each other.
Want to learn more about the EngEd Program?
Discover Section’s community-generated pool of resources from the next generation of engineers.
Источник
Safe Args — верный помощник Navigation Component
В этой статье вы узнаете, кто такой этот Safe Args, как он упрощает жизнь и что является продуктом его работы, в том числе и за кулисами.
Вы сейчас во второй части большого материала про Navigation Component в многомодульном проекте. Если вы впервые слышите про Navigation Component, то рекомендую сначала почитать, что вообще такое Navigation Component. Если уже знакомы с азами, то можно переходить к самому интересному:
Safe Args — это плагин, идущий отдельно от Navigation Component, но созданный специально для того, чтобы с библиотекой работалось легче. С ним нет необходимости указывать id destination-а и передавать параметры через Bundle — плагин генерирует для этого отдельные классы и имеет набор extension-ов для работы с ними. Давайте разбираться, как это всё работает.
Show me the code!
Здесь мы взяли: достаточно простой граф, где есть фрагмент, вложенный граф и переход от одного к другому. Единственная особенность — пресловутый , с помощью которого мы передаем в users details фрагмент параметр userId. Пересоберём проект и посмотрим, что получилось.
В generated-папке модуля, где лежит граф, находим вот такой класс — обертку, которая включает в себя все переходы, указанные в графе с уже включенными туда параметрами. В нашем случае это переход на экран деталей пользователя и передача туда аргумента userId.
Теперь вызов перехода будет выглядеть так:
А параметры в целевом destination-е можно получить через extension, который теперь у нас имеется.
Сам класс аргументов тоже генерируется в отдельный класс, который содержит методы упаковки и распаковки параметров в Bundle, но он уже не так интересен.
В итоге
Safe Args — приятное дополнение к Navigation Component, благодаря которому мы облегчили себе работу с id переходов и обработки получения/отправки их аргументов. Использовать его или нет — дело ваше, но дальнейшее повествование основано на использовании это плагина. Спойлер: он принесет немало проблем, но в конце все будут счастливы 🙂
Источник
Using Safe Args With the Android Navigation Component
In this Safe Args tutorial, you’ll learn how to safely pass data between different destinations while using the Android Navigation Component.
Version
- Kotlin 1.4, Android 10.0, Android Studio 4.2
Passing data between screens is a common use case in an Android app. This enables destinations to communicate with each other and builds a continuous flow inside an app. Google recommends using the Android Navigation Component to manage navigation between destinations in your app. In this tutorial, you’ll learn to use this component with Safe Args to pass data while navigating between screens.
You’ll build SafeFly, an app that lets you buy plane tickets. The first screen retrieves the user’s travel information, then passes it to a confirmation screen. During the process, you’ll learn:
- Why you should use Safe Args.
- How to pass data using Safe Args.
- Things to consider when using Safe Args and code shrinking in the same app.
Getting Started
Download the materials using the Download Materials button at the top or bottom of this tutorial. Open Android Studio and import the starter project.
Take a moment to familiarize yourself with the code. You’ll see the following classes:
- TravelInformationFragment.kt: Fragment where the user enters their information.
- ConfirmationFragment.kt: Fragment that displays the user’s travel information to confirm it.
- TravelerInformation.kt: Data class that wraps personal user information required for purchasing a plane ticket.
Build and run the project. You’ll see a screen that allows the user to enter their information, which travel add-ons they want and a promo code, if they have one. On the next screen, you’ll notice the fields are all empty. The user’s information should display instead. That’s what you’ll handle next.
Why Safe Args?
Before the introduction of Safe Args, passing data when navigating to a different screen required Bundle s. The first screen, the sender, would build a Bundle instance and populate it with data. The second screen, the receiver, would later retrieve this data. This manual approach of sending and unwrapping data is unreliable, as the sender and receiver must agree on:
- The keys.
- The default values for each key.
- The type of data corresponding to the keys.
There’s also no way for the receiver to force the sender to pass all the required data. Furthermore, when unwrapping the data, type safety isn’t guaranteed on the receiver’s end.
Android introduced Safe Args to resolve these issues. Safe Args is a Gradle plug-in that generates code to add data to a Bundle and get it back in a simple and type-safe manner.
Now that you see why Safe Args is useful for your project, it’s time to implement it.
Adding Safe Args to Your App
To add Safe Args to your project, include the following classpath in your top-level build.gradle:
Apply Safe Args’s Kotlin plug-in by adding the following line to your app-level build.gradle at the top:
The plug-in above generates Kotlin code, so you should use it in Kotlin-only modules, such as in this tutorial.
If your module uses Java or a mix of Java and Kotlin, apply the following plug-in, which generates Java code:
Click Sync now and wait for Gradle to sync the dependencies.
Passing Data With Safe Args
The data you pass from one destination to another is called an argument. This data can be a simple number or a complex model. Note that passing the minimal amount of data necessary between destinations is a best practice, because there’s a limit to the total space available for all saved states.
An argument can be of any type that Bundle supports, including:
- Integer
- Float
- Long
- Boolean
- String
- resource reference
- Parcelable
- Serializable
- Enum
Primitive types — int , float , long and bool — back Integer , Float , Long and Boolean respectively on the JVM, so they can’t be null. In contrast, String , Parcelable and Serializable can accept null values.
You can provide a default value to an argument, which it will take if the sender doesn’t assign a value at runtime.
An argument can also be an array of any of the above types, except resource references and Enums . If an argument is an array, it can have a null value, regardless of its underlying type. Additionally, it can only have a null default value.
Defining an Argument in a Destination
The confirmation screen in the app expects to receive certain information: the traveler’s information, travel add-ons and promo code. The navigation graph should define these three arguments. In this section, you’ll see how to add arguments using the editor.
Open navigation_graph.xml, select the Design window at the top-right corner and click ConfirmationFragment .
On the right, you’ll notice the Arguments tab, which has a + button to its right. Click the button to add the first argument, the promo code. In the Add Argument dialog, do the following:
- Set its name to promoCode.
- Select its type to String.
- Set it to Nullable.
- Set its default value to @null since the argument is optional.
Click Add. Congrats, you just created your first argument!
Now, switch to the Code window by selecting it at the top-right corner and notice that inside ConfirmationFragment , a new argument appears:
That’s quite readable and straightforward. :]
Switch back to the Design window and add the add-ons argument. Call it travelAddOns, set its type to Integer since it should contain multiple values and set it to be an array. Once added, there should be a new argument as follows:
There goes your second argument!
Adding a Custom Type Argument
It’s time to create the last argument for the traveler’s information, but what would its type be? It should be of type TravelerInformation , which is a data class that’s in the project. Remember that to pass it with Safe Args, it has to be one of the supported types. You’ll have to make TravelerInformation a Parcelable . To do this, add the following plug-in to the app-level build.gradle :
After syncing the project, open TravelerInformation.kt and make the class a Parcelable . Annotate it with @Parcelize and have it implement the Parcelable interface. The class should now be as follows:
In the code above, adding @Parcelize generates the code required to make TravelerInformation parcelable.
You’re now ready to set TravelerInformation as an argument type.
Go back to navigation_graph.xml and create the last argument for the traveler’s information. Call it travelerInformation, select the argument type custom parcelable, select the class TravelerInformation and click Add. Build the project and let Safe Args do its magic.
Retrieving Arguments in a Destination
Since you’ve defined arguments for the ConfirmationFragment destination, Safe Args will generate a class named ConfirmationFragmentArgs , whose name is the destination ConfirmationFragment and the suffix Args. This class has a static method fromBundle(Bundle) . You’ll use it to retrieve the arguments ConfirmationFragment needs.
To do this, open ConfirmationFragment.kt and replace the TODO on line 64 with the following:
In the code above, you’re:
- Getting the fragment’s arguments, assigning them to the variable bundle and making sure it isn’t null. If it is, the program logs the error and returns from the method.
- Using ConfirmationFragmentArgs.fromBundle() , which Safe Args generated. You pass it the non-null arguments this Fragment received, then retrieve each piece of information it expects: the traveler’s information, the travel add-ons and the promo code.
Build and run the app. You’ll notice that the app crashed on clicking Next. This is because the confirmation screen expects data you haven’t sent yet. You’ll wire both screens in the next steps!
Defining an Argument in an Action
Just as the confirmation screen specifies the arguments it expects to receive, the action that navigates from the information screen to the confirmation screen has to define those same arguments. This ensures the sender — the travel information screen — is sending the correct information.
In the previous step, you added arguments using the editor. In this step, you’ll add them using XML, which is just as easy!
You’ll again need to define the three pieces of information to send: the traveler’s information, the travel add-ons and the promo code. Open navigation_graph.xml, select the Code window from the top-right corner and scroll down to the action block.
Start by defining the promo code argument using the code below:
The argument definition above specifies:
- The argument’s name.
- The argument’s default value. Since the promo code is optional, you set it to null in case the user doesn’t enter one.
- The argument’s type.
- The argument’s nullability. If the traveler doesn’t have a promo code, it appears as null.
This is the exact definition of the promo code argument inside ConfirmationFragment .
As an exercise, add the remaining two arguments: the traveler’s information and the add-ons. If you get stuck, check how they’re defined inside ConfirmationFragment .
Need help? Just open the spoiler below to find out how.
Here are the arguments to add below the previous one:
Once you’re done, build and run to let Safe Args do its magic yet again.
Sending Arguments From a Destination
This time, Safe Args will generate the class TravelInformationFragmentDirections , whose name is the initial destination TravelInformationFragment with the suffix Directions. Inside this class is actionTravelInformationFragmentToConfirmationFragment , which is the name of the navigation action defined in navigation_graph.xml. This method takes all the mandatory data it will pass to the confirmation screen. Note that mandatory arguments are those that don’t have a default value.
Open TravelInformationFragment.kt and replace the TODO on line 71 with the following:
Here’s what’s happening in the code above:
- You read the information the user entered on the screen, which you must pass to the next screen. This includes their traveler information, the travel add-ons they selected and their promo code.
- You build a NavDirections instance that wraps the arguments passed during navigation. Note that the order of arguments has to match the order in navigation_graph.xml.
Now, change the argument of findNavController().navigate() from R.id.action_travelInformationFragment_to_confirmationFragment to directions . The method invocation should now appear as follows:
In the code above, you use NavDirections , which you created earlier, to specify the destination and the arguments to pass.
Build and run. Enter data on the first screen, select a couple of add-ons, optionally input a promo code and navigate to the following screen. You’ll see the information you’ve entered display correctly. The app’s finally functional!
Safe Args and Proguard
When using Parcelable , Seriazable and Enum types with Safe Args, you specify the type’s name in the navigation graph as you define the argument. The mapping between these types and their corresponding classes in your project won’t persist if your project uses obfuscation. Therefore, you must prevent the minification process at build time from obfuscating these classes.
SafeFly uses obfuscation and code shrinking, as you may have noticed in build.gradle .
It passes a TravelerInformation instance when navigating between screens in the app. There are two approaches you can use to prevent obfuscation from occurring. The first is annotating the class with @androidx.annotation.Keep in TravelerInformation.kt:
The second is adding keepnames rules to proguard-rules.pro :
Implement either of these options, go to the bottom-left corner of Android Studio, click Build Variants and select release from Active Build Variant.
Build and run. This runs a release build with code that was shrunk and obfuscated. Make sure you can still enter information on the first screen and see it displayed on the confirmation screen.
Where to Go From Here?
Download the final project using the Download Materials button at the top or bottom of the tutorial.
Congratulations! You learned why you should use Safe Args with Android’s Navigation Component. You also saw how to create arguments and pass data between destinations. Additionally, you learned how to properly handle using Safe Args and obfuscation in your app.
If you need to brush up on Android’s ‘Navigation Component or want to learn about advanced topics such as deep links and transitions, check out The Navigation Architecture Component Tutorial series of articles.
I hope you enjoyed this tutorial! If you have any questions or comments, please join the forum discussion below.
Источник