38

I'm using the Jetpack Navigation Component in my project with a single activity and some fragments.

I have a fragment with a list that fills from server side. I call getDataFromServer on the onViewCreated method then, when a user clicks on an item, a new fragment shows.

The problem is that when I press the back button, onViewCreated is called again in my list fragment.

So, how can I prevent my first fragment from recreating again? I don't want unnecessary onViewCreated calls.

FarshidABZ
  • 3,860
  • 4
  • 32
  • 63
  • use onActivityCreated function by overriding it in the fragment of yours and get your getDataFromServer there maybe! – Rizwan Atta Feb 06 '19 at 12:11
  • @Rizwanatta it is a good trick, I'll do it, but I'm waiting for maybe better answers to .thanks – FarshidABZ Feb 06 '19 at 12:14
  • 1
    Since you are using Navigation Component, you should probably use ViewModels aswell. It makes live much more easier – egoldx Feb 06 '19 at 12:51
  • This is why they invented `ViewModel` + `fragment.getViewLifecycleOwner()`. – EpicPandaForce Feb 07 '19 at 02:54
  • so where should I tell to ViewModel to get the data? @EpicPandaForce – FarshidABZ Feb 07 '19 at 08:34
  • 1
    You can take some inspiration from [NetworkBoundResource, where it is an effect of observing the LiveData inside the ViewModel](https://github.com/googlesamples/android-architecture-components/blob/103dac5015f51e179039c45c0184418c368ee922/GithubBrowserSample/app/src/main/java/com/android/example/github/repository/NetworkBoundResource.kt#L42) – EpicPandaForce Feb 07 '19 at 09:59
  • @EpicPandaForce thanks, google sample was good, I think I have to get the first page of my list in view model constructor, and for next pages, I can do just like pagination. hope view model doesn't create again :D, because, in google sample, ViewModel initialized at onActivityCreated, but in my case list fragment shown if the user clicked on some buttons – FarshidABZ Feb 07 '19 at 11:30
  • If ```popStackBack()``` is called instead your old fragment will only get its ```onResume()``` called instead, else if it had been destroyed you need override ```onSaveInstanceState/onRestoreInstanceState``` – Marcos Vasconcelos Feb 20 '20 at 22:20

4 Answers4

7

Of course, we can not prevent calling oncrateView, but there is a simple way. Instead of calling view.loadData() in onCreateView or other lifecycle methods we can call it while initializing ViewModel

this article helped me to know ViewModel better 5 common mistakes when using Architecture Components

Update:

The current navigation component (V 2.3.0) doesn't support this feature, it always kills the fragment while navigating to another fragment. Imagine you have google map in Fragment A so each time you returns to the Fragment it initialized again and the camera moves to the user location!! (what a bad idea).

So the best way is not to use the navigation component if you have the same issue.

Navigation, Saving fragment state, GitHub issue

Update 2:

In some cases like Filters or pagination, we can use Transformations like switchMap in our ViewModel instead of getting data in init function.

Update 3:

If you have to call a function to load data from a source, there are lots of ways to prevent call that function again, the first and easiest way is instead of calling getData() in your view, do it your ViewModel init function. the second one is using lazy variables and another one is using SwitchMap on livedata. for more information you can find all solutions here

FarshidABZ
  • 3,860
  • 4
  • 32
  • 63
0

You can't prevent calling onViewCreated method or any method of your fragment when back button pressed so you should better use view model with your list fragment and get data from server in your view model. Avoid getting data from server in your fragment since you already using Navigation UI.

Hussnain Haidar
  • 2,200
  • 19
  • 30
  • so where should I tell to ViewModel to get the data? – FarshidABZ Feb 07 '19 at 08:04
  • In view model class. maybe you learn about view model it's a better approach. – Hussnain Haidar Feb 07 '19 at 08:46
  • I mean in fragment where should I tell to view model? I have pagination too. ok in my view model I get the data, but when fragment recreate again in the onViewCreated method I tell ViewModel to get the data, and it gonna get it, but I have it, and also I have pagination, How can I know that getting data is for pagination or not? – FarshidABZ Feb 07 '19 at 09:11
  • 1
    look at this gist [viewmodel](https://gist.github.com/haidar786/dc17dd768b3f71dfa8fe58df860e278b) and [mainFragment](https://gist.github.com/haidar786/3bde07a75a372f99c9146c8ff6eb60c8) – Hussnain Haidar Feb 07 '19 at 09:24
  • Even with `ViewModel` it doesn't prevent the fact that the base/home fragment view is being recreated which can be very expensive right? – Mihae Kheel Apr 23 '21 at 16:18
0

Maybe you have activate the graph.

app:popUpTo="@+id/nav_fingerprint_capture"
app:popUpToInclusive="true"
-1

Jetpack navigation component replaces the fragments. It does not add the fragments to stack. So when you open Fragment B from Fragment A and click back button in Fragment B, then Fragment A gets recreated.

If there is an API call in Fragment A and you do not want to make API call on every fragment recreation then you can save the API response in viewmodel and reuse the response when fragment gets recreated.

Example:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    if(foodViewModel.foodDataList.isEmpty()) {
        //make API call here and
        //save the response in foodViewModel.foodDataList
    } else {
        //use the saved response from viewmodel
        //and populate recyclerview
    }
Ramakrishna Joshi
  • 1,442
  • 17
  • 22
  • 1
    This does not prevent the fact that you are populating the fragment view once more which can be very expensive. – Mihae Kheel Apr 23 '21 at 16:17
  • @MihaeKheel Yes, I know that. I do not see any other possible solution for this problem. By nature, Jetpack Navigation replaces fragments. – Ramakrishna Joshi Apr 18 '23 at 10:50