Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

getViewLifecycleOwner() in DialogFragment leads to crash

I use DialogFragment (onCreateDialog) and ViewModel for it. But, when I try to pass getViewLifecycleOwner() to the LiveData::observe method I get the error below:

java.lang.IllegalStateException: Can't access the Fragment View's LifecycleOwner when getView() is null i.e., before onCreateView() or after onDestroyView().

Is it possible to use getViewLifecycleOwner() inside a DialogFragment?

like image 498
Alex.Marynovskyi Avatar asked Feb 19 '19 12:02

Alex.Marynovskyi


People also ask

What is DialogFragment used for?

DialogFragment is a utility class which extends the Fragment class. It is a part of the v4 support library and is used to display an overlay modal window within an activity that floats on top of the rest of the content. Essentially a DialogFragment displays a Dialog but inside a Fragment.

Can multiple observers be attached to a live data instance?

All observers will only receive events once they happen! Next use it just as you would regular live data: Multiple observers receive events. In case of same multiple observer problem(fragment back-stack) it is possible to use observeInOnStart() method.

How do I start a DialogFragment?

To create a DialogFragment , first create a class that extends DialogFragment , and override onCreateDialog() , as shown in the following example. Similar to how onCreateView() should create a root View in an ordinary fragment, onCreateDialog() should create a Dialog to display as part of the DialogFragment .


3 Answers

This happens because of how the DialogFragment is created. If you use onCreateDialog() than a slightly different lifecycle is used for this type of Fragment. The onCreateView() will not be used, thus the viewLifecycleOwner for this Fragment won't be initialized.

As a workaround for this, you can use the Fragment instance as the owner for the observer: .observe(this, Observer {...}. Although you will get a warning for using this instead of the viewLifecycleOwner.

like image 70
Ionut Negru Avatar answered Oct 16 '22 14:10

Ionut Negru


This is the official recommendation for DialogFragments:

Note: When subscribing to lifecycle-aware components such as LiveData, you should never use viewLifecycleOwner as the LifecycleOwner in a DialogFragment that uses Dialogs. Instead, use the DialogFragment itself, or if you're using Jetpack Navigation, use the NavBackStackEntry.

So you can just observe things as normal, but instead of viewLifecycleOwner you pass this, or the current backstack entry (e.g. findNavController().currentBackStackEntry). No need to override onCreateView just to force a viewLifecycleOwner to be created or anything!

like image 27
cactustictacs Avatar answered Oct 16 '22 16:10

cactustictacs


Your case is slightly different but I think the concept is kind of the same. Just use this.getActivity() in your Dialog Class and pass it as LifeCycleOwner. I had the same problem because I used LiveData and Retrofit and LiveData needs a reference. The DialogFragment sets its LifeCycleOwner at some point but it is not at any of the methods mentioned above. By using the getActivity() you can use your observer as early as in onCreateDialog method. Here is some portion of my code that at first caused some issue when I tried to pass a null referenced this.getViewLifecycleOwner() instead of the activity.

@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
       FragmentActivity activity = this.getActivity();
       binding = DialogSelectIssuesBinding.inflate(LayoutInflater.from(getContext()));

       RetroRepository.
            getDefault().
            getAllIssues().
            observe(this.getActivity(), listLiveDataResponse -> {
                //ToDo Check for errors and Bind the data here 
            });


       AlertDialog alertDialog = new AlertDialog.Builder(activity)
                            .setView(binding.getRoot())
                            .setTitle("Please select issues from the list below:")
                            .setNegativeButton("CANCEL", null)
                            .setPositiveButton("ADD", null)
                            .create();
       alertDialog.setCanceledOnTouchOutside(false);
       return alertDialog;
}
like image 8
Kamyar Miremadi Avatar answered Oct 16 '22 16:10

Kamyar Miremadi