onGetLayoutInflater() cannot be executed until the Fragment is attached to the FragmentManager
Asked Answered
K

4

14

I sometime get an Exception with the following message:

onGetLayoutInflater() cannot be executed until the Fragment is attached to the FragmentManager

My full stacktrace (which is using CompositeAndroid as the parent fragment) :

Fatal Exception: java.lang.IllegalStateException: onGetLayoutInflater() cannot be executed until the Fragment is attached to the FragmentManager.
       at android.support.v4.app.Fragment.getLayoutInflater(Fragment.java:1151)
       at com.pascalwelsch.compositeandroid.fragment.CompositeFragment.super_getLayoutInflater(CompositeFragment.java:1310)
       at com.pascalwelsch.compositeandroid.fragment.FragmentDelegate$7.call(FragmentDelegate.java:204)
       at com.pascalwelsch.compositeandroid.fragment.FragmentDelegate$7.call(FragmentDelegate.java:197)
       at com.pascalwelsch.compositeandroid.fragment.FragmentPlugin.getLayoutInflater(FragmentPlugin.java:149)
       at com.pascalwelsch.compositeandroid.fragment.FragmentPlugin.getLayoutInflater(FragmentPlugin.java:1269)
       at com.pascalwelsch.compositeandroid.fragment.FragmentDelegate$7.call(FragmentDelegate.java:202)
       at com.pascalwelsch.compositeandroid.fragment.FragmentDelegate$7.call(FragmentDelegate.java:197)
       at com.pascalwelsch.compositeandroid.fragment.FragmentDelegate.getLayoutInflater(FragmentDelegate.java:208)
       at com.pascalwelsch.compositeandroid.fragment.CompositeFragment.getLayoutInflater(CompositeFragment.java:163)
       at android.support.v4.app.Fragment.onGetLayoutInflater(Fragment.java:1101)
       at com.pascalwelsch.compositeandroid.fragment.CompositeFragment.super_onGetLayoutInflater(CompositeFragment.java:1710)
       at com.pascalwelsch.compositeandroid.fragment.FragmentDelegate$32.call(FragmentDelegate.java:769)
       at com.pascalwelsch.compositeandroid.fragment.FragmentDelegate$32.call(FragmentDelegate.java:762)
       at com.pascalwelsch.compositeandroid.fragment.FragmentPlugin.onGetLayoutInflater(FragmentPlugin.java:528)
       at com.pascalwelsch.compositeandroid.fragment.FragmentPlugin.onGetLayoutInflater(FragmentPlugin.java:1456)
       at com.pascalwelsch.compositeandroid.fragment.FragmentDelegate$32.call(FragmentDelegate.java:767)
       at com.pascalwelsch.compositeandroid.fragment.FragmentDelegate$32.call(FragmentDelegate.java:762)
       at com.pascalwelsch.compositeandroid.fragment.FragmentDelegate.onGetLayoutInflater(FragmentDelegate.java:773)
       at com.pascalwelsch.compositeandroid.fragment.CompositeFragment.onGetLayoutInflater(CompositeFragment.java:538)
       at android.support.v4.app.Fragment.performGetLayoutInflater(Fragment.java:1132)
       at android.support.v4.app.Fragment.getLayoutInflater(Fragment.java:1117)
       at com.my.app.features.event.EventDetailFragment.attachHeader(EventDetailFragment.java:66)
       ...

We can see here that we first call the method getLayoutInflater() at line 1117 and then the one at line 1151.

The first one is this one:

/**
 * Returns the cached LayoutInflater used to inflate Views of this Fragment. If
 * {@link #onGetLayoutInflater(Bundle)} has not been called {@link #onGetLayoutInflater(Bundle)}
 * will be called with a {@code null} argument and that value will be cached.
 * <p>
 * The cached LayoutInflater will be replaced immediately prior to
 * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} and cleared immediately after
 * {@link #onDetach()}.
 *
 * @return The LayoutInflater used to inflate Views of this Fragment.
 */
public final LayoutInflater getLayoutInflater() {
    if (mLayoutInflater == null) {
        return performGetLayoutInflater(null);
    }
    return mLayoutInflater;
}

And the second one is the one that throws the Exception and that is marked as deprecated:

/**
 * Override {@link #onGetLayoutInflater(Bundle)} when you need to change the
 * LayoutInflater or call {@link #getLayoutInflater()} when you want to
 * retrieve the current LayoutInflater.
 *
 * @hide
 * @deprecated Override {@link #onGetLayoutInflater(Bundle)} or call
 * {@link #getLayoutInflater()} instead of this method.
 */
@Deprecated
@NonNull
@RestrictTo(LIBRARY_GROUP)
public LayoutInflater getLayoutInflater(@Nullable Bundle savedFragmentState) {
    if (mHost == null) {
        throw new IllegalStateException("onGetLayoutInflater() cannot be executed until the "
                + "Fragment is attached to the FragmentManager.");
    }
    LayoutInflater result = mHost.onGetLayoutInflater();
    getChildFragmentManager(); // Init if needed; use raw implementation below.
    LayoutInflaterCompat.setFactory2(result, mChildFragmentManager.getLayoutInflaterFactory());
    return result;
}

Is it normal that their is a call to a deprecated function here? And I think it shouldn't throw the Exception because I'm checking for isAdded() before calling getLayoutInflater():

private void init () {
    if(isAdded()) {
        attachHeader();
        updateHeader();
    }
}

private void attachHeader() {
    headerBinding = DataBindingUtil.inflate(getLayoutInflater(), layout.event_detail_header,
            binding.formsContainer, false);
    binding.formsContainer.addView(headerBinding.getRoot(), 0);
}
Kob answered 7/5, 2018 at 12:5 Comment(0)
H
3

For future readers . In my case . I was using getLayoutInflater in Fragment inside Api response and when I try to change the fragment , I was getting this error

Fatal Exception: java.lang.IllegalStateException: onGetLayoutInflater() cannot be executed until the Fragment is attached to the FragmentManager.

So I Solved this issue by just Cancelling my Api call onDestroy of fragment

 @Override
    public void onDestroy() {
        super.onDestroy();
        disposable.dispose(); //RxJava
    }

Might be helpful to other user.

Haroldharolda answered 22/3, 2019 at 10:35 Comment(1)
Generally, it is recommended to do all actions in onDestroy, before calling super.onDestroy and doing all actions in onCreate after calling super.onCreate. Since the parent may inform the OS that onDestroy is finished at the end of it's own method, calls happening after that may never be executed, if the system cleans up too fast. Also, bindings and views may not exist anymore, even though this doesn't affect disposables.Toile
A
1

You must first make sure that the Fragment is attached to the FragmentManager before the mHost.onGetLayoutInflater() use this to check the fragment is attached:

    Activity activity = getActivity();
    if (isAdded() && activity != null){ // Check the fragment status
        LayoutInflater result = mHost.onGetLayoutInflater();
        getChildFragmentManager(); // Init if needed; use raw implementation below.
        LayoutInflaterCompat.setFactory2(result, 
        mChildFragmentManager.getLayoutInflaterFactory());
    }else{
        LayoutInflater result = null;
    }
Azeria answered 21/3, 2019 at 11:3 Comment(0)
A
1

I feel this may still help somebody. Use of inflater from the onCreateView whilst binding views seems to solve this error - Fatal Exception: java.lang.IllegalStateException: onGetLayoutInflater() cannot be executed until the Fragment is attached to the FragmentManager.

@Override
            public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            render(inflater);
        }
    
    private void render(LayoutInflater inflater) {
    GenericViewBinding genericViewBinding =  GenericViewBinding .inflate(inflater);
    }

In my case, this error occurred inside(Using Google firestore which has offline cache turned on by default which makes responses feel instantaneous) documentReference.addSnapshotListener(new EventListener() { } when I tried to inflate views when my fragment wasn't yet attached to the FragmentManager.

With all humility I write, please don't be bashful. I am just trying to help. Perhaps there is something I still don't understand. I would still love to hear from you. Thank you.

Acetum answered 31/8, 2020 at 13:17 Comment(0)
Y
0

Use the following code to check if the Fragment has been destroyed before using it:

Kotlin:

if (
    activity?.isFinishing == true ||
    activity?.isChangingConfigurations == true || 
    this.isRemoving || this.isDetached || !this.isAdded || 
    this.activity == null || this.view == null) {
    return
} else {
    Todo()
}
Yapok answered 4/10, 2021 at 4:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.