How are Android activities handled with Jetpack Compose and Compose Navigation?
The Compose application is designed to be used in a single-activity architecture with no fragments.
You can still have multiple activities or fragments and use setContent
in each of them, but in this case the transfer of data between activities falls on your shoulders. Use this approach if you're adding new Compose screens to an existing application built the old way.
But with Compose, it's much easier to do all the navigation within a single activity using Compose Navigation. Much less code, better performance due to no unnecessary code layers, easy to transfer data, etc.
To work with the view lifecycle, check out compose side-effects:
LaunchedEffect
can be used to execute an action when the view appears. It also runs on a coroutine context that is bound to the current composable: you can easily run suspend functions, and when the view disappears from view hierarchy - the coroutine will be canceled.DisposableEffect
can be used to subscribe to/unsubscribe from callbacks.
When you rotate the screen, all effects will restart no matter which key you passed.
@Composable
fun MainScreen(navController: NavController) {
LaunchedEffect(Unit) {
println("LaunchedEffect: entered main")
var i = 0
// Just an example of coroutines usage
// don't use this way to track screen disappearance
// DisposableEffect is better for this
try {
while (true) {
delay(1000)
println("LaunchedEffect: ${i++} sec passed")
}
} catch (cancel: CancellationException) {
println("LaunchedEffect: job cancelled")
}
}
DisposableEffect(Unit) {
println("DisposableEffect: entered main")
onDispose {
println("DisposableEffect: exited main")
}
}
}
Also note that in both cases, and in many other cases in compose, you pass key
to these functions. This helps compose understand when the value should be recomputed. In my example it is Unit
, which means that it won't change until the view is gone. But if you create a remember
value, use another dynamic value from the view model, or pass another argument to composable, you can pass it as a key
, this will cancel the current LaunchedEffect
job and call onDispose
for DisposableEffect
, and your job will be restarted with the updated key
value. You can pass as many keys as you want.
Read more about the state in Compose in documentation.
Jetpack Compose activities and composables for large projects
It is not a particularly bad practice to make your app multi-activity-embedded, but the thing is -- In most of the cases, you do not need it. The users do not know what an activity is, since they simply look at the app in terms of the screens that the app offers. Therein lies the explanation. A single Composable can represent an entire screen super-easily, it's barely an inconvenience.
HENCE, the entire idea and need of multiple activities gets destroyed. Single activity apps are more efficient too, since there's only one activity so you do not have to explicitly handle the data-flow between various activities. This means a single ViewModel, minimal coupling, high code-readability, and easy data-flow.
This is the same reason that fragments are discouraged too. They often involve multiple viewModels which complicates things. When a Composable
can accomplish everything on its own, it automatically becomes the best choice for developers to use to build screens. I would highly recommend, no matter the size of your project, that you stick to a single activity.
I can understand your viewpoint as well, and I have something to help.
The Navigation Codelab clearly and simply explains the Navigation System in Compose, which is a framework that basically allows you to create multiple screen-representing (or other) Composables, and then navigate to and from them from anywhere IN THE OS! It is actually super-easy to use, barely an inconvenience.
Jetpack Compose, using new Activity or new Navigation?
Jetpack Compose is built for single activity architecture what that means is you only have one activity and use compose navigation for navigating in your apps it is much easier also but if you need to use multiple activities you can but the data passing falls on your hands you need to take care of it.
How to handle navigation in Jetpack Compose?
New Jetpack lib has published for Compose navigation. It is still in alpha.
In this new library, now user can able to navigation between different composables with navigation components features.
Using navigation-compose:
dependencies {
def nav_compose_version = "1.0.0-alpha01"
implementation "androidx.navigation:navigation-compose:$nav_compose_version"
}
Example:
Step 1: create a NavController
by using the rememberNavController()
method in your composable: Link:
val navController = rememberNavController()
Step 2: Creating the NavHost
requires the NavController
previously created via rememberNavController()
and the route of the starting destination of your graph:Link.
NavHost(navController, startDestination = "profile") {
composable("profile") { Profile(...) }
composable("friendslist") { FriendsList(...) }
...
}
Step 3: To navigate to a composable use navigate()
:
fun Profile(navController: NavController) {
...
Button(onClick = { navController.navigate("friends") }) {
Text(text = "Navigate next")
}
...
}
check more https://developer.android.com/jetpack/compose/navigation
Jetpack Navigation Component with Jetpack Compose compatible?
Long-term, there will be first-class support in the Navigation component for Jetpack Compose. Google has indicated this on several occasions, mostly in the Kotlinlang Slack #compose
channel.
Right now (30 June 2020), Navigation knows nothing about Compose. Eventually, my guess is that the Navigation DSL will support referencing composables (or lambdas that update states), but that is just a guess.
If you want to use both today, one option would be to continue using fragments:
- Have Compose define the UI for those fragments
- Have Navigation route between the fragments
Later, when Navigation directly supports Compose, you could "unwind" those fragments and do everything directly in Compose+Navigation.
Alternatively, there are non-Navigation routing options available from third parties (though, like Compose, they are all fairly new).
Use NavHostFragment in Jetpack Compose
You are correct that you cannot use rememberNavController()
- that will create a completely new nested NavController
suitable only for composable destinations.
As per the testing guide, it is strongly recommended to avoid any direct references to any NavController
within any of your composables themselves (i.e., a HomeScreen
composable). Instead, the recommendation is to pass in a lambda that your composable can trigger when it wants to navigate.
Your Fragment (the one calling setContent
on your ComposeView
) would then be responsible for implementing that lambda and calling NavHostFragment.findNavController(this).navigate(directionToFragment2)
.
As a (less recommended) alternative, you can also use LocalView.current.findNavController()
as Fragments populate the NavController
at the view level as well and LocalView
points to the ComposeView
hosting your Composable.
Related Topics
How to Detect Android CPU Speed
What Is the Real World Accuracy of Phone Accelerometers When Used for Positioning
Gradle 0.9.+/0.9.2 Error: Unable to Load Class 'Com.Android.Builder.Testing.Api.Deviceprovider'
How to Add Code Obfuscation for My Android Application
Out of Memory While Creating Bitmaps on Device
Get List of Active Pendingintents in Alarmmanager
Android Studio: How to Generate Signed APK Using Gradle
How to Implement Multi-Selection and Contextual Actionmode in Actionbarsherlock
Why a Viewmodel Factory Is Needed in Android
How to Send Hashmap Value to Another Activity Using an Intent
App Getting Stuck with E/Com.Facebook.Internal.Attributionidentifiers
Writing Text File to Sd Card Fails
How to Show Daily Offline Notifications in Android 10
Finding Current Location of the User in Android
Android Opengl .Obj File Loader
Android - Copy Existing Project with a New Name in Eclipse
On Showing Dialog I Get "Can Not Perform This Action After Onsaveinstancestate"