Friday, November 29, 2019

IllegalStateException: The specified child already has a parent


If your app suddenly starts crashing with the following exception, and you have ruled out the usual suspects, its probably due to an appcompat issue.

java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first

I updated our andridx.appcompat version from 1.0.0 to 1.1.0 and was greeted with this crash. Unfortunately for me, that commit included multiple changes like enabling leak canary, updating the versions of various dependency libraries, and enabling multidex. So it took me some time to narrow down the offending change. The crash went away when I rolled back appcompat version to 1.0.0


But I want to use the latest version of appcompat!

In my case, the root cause was that we were reusing fragment's layout view object. In the first call to onCreateView, I'd inflate the layout xml and store the resulting view in a field. In the If the subsequent calls to this method, we would just return it instead of inflating again. Something like the following snippet. 

public View onCreateView(LayoutInflater inflater,
                         ViewGroup container, Bundle savedState) {
    if (mFragmentView != null) {
        return mFragmentView;
    }
    mFragmentView = inflater.inflate(R.layout.fragment_main, container, false);
    return mFragmentView;
}

The crash happens when `mFragmentView` is not null. This happens whenever the user navigates back to this fragment using the back button. I removed that line so that we always inflated the layout, and it fixed the crash. Looks like reusing is not encouraged in this context. I was relying on this logic to preserve the fragment's state (text etc) in some cases, so I think it is time for a 'minor' refactoring.

Removing the root view from its parent in onDestroyView also seems to work, but that sounds like cheating. I am going to keep that change on a branch until I figure out why appcompat team decided that we should not be reusing fragment layout objects like this. I couldn't find anything on StackOverflow that explains this behaviour. If you think reusing layouts is not a good idea, or know why appcompat changed its behaviour, please leave a comment.