The most frequently-occurring crash in our app is originating at android.support.v4.app.FragmentManagerImpl.moveToState. The crash itself is:
Caused by: java.lang.IllegalArgumentException: No view found for id 0x7f080000 (com.fragmentcrash.fragmentmanagerimplcrash:id/some_fragment_container) for fragment Fragment{41f0b5f8 #0 id=0x7f080000 some_fragment}
Thankfully, this line includes the id of the layout involved as well as the tag of the fragment involved, which allowed us to (eventually) replicate the crash. This helped us to narrow it down to a specific Activity, and I was able to replicate it on my Nexus 7, but not on any smaller devices. The Crashlytics page for this crash corroborates this: it only occurs on sw >= 600dp devices (Nexus 7, Galaxy Tab 3, Galaxy Tab 10, etc.). The exact steps to reproduce are as follows:
- Open the app (any orientation).
- Lock the screen.
- Ensure the device is in landscape orientation.
- Unlock the screen.
The crash occurs the moment you press the unlock button (i.e. when you are presented with your device's unlock screen, before the app has returned to the foreground). So, what is the app doing to make this happen? We have two layouts for this Activity, one in layout/ and another in layout-sw600dp-port/. The Activity is locked in portrait orientation via its entry in AndroidManifest.xml. The sw600dp layout has a FrameLayout that gets used as the container for a programmatically-added Fragment that the regular sized layout does not. The Fragment is only created and attached if the smallest width is 600dp (determined at runtime). Curiously, if you add that FrameLayout to the regular-sized layout (the one in layout/), the crash ceases to occur. It appears that, in the absence of a sw600dp landscape layout, the base layout is being inflated, even though it should be using the portrait layout because the Activity is restricted to portrait, and even though it's not technically in the foreground yet. It's worth noting that this only happens when unlocking the screen in landscape - if you are running the app in the foreground and simply rotate the device to landscape, nothing happens. This was easy enough to fix (we finally implemented our intended landscape layout for that Activity, which is the whole reason the initial layout was in layout-sw600dp-port/ rather than just layout-sw600dp/). However, I'm curious to understand why it was happening in the first place.
Why does Android seem to inflate an incorrect layout before applying the specified orientation restriction, and why does it happen before the user dismisses their lock screen?
If you want to replicate this crash for yourself, I've made a project with the bare minimum required to do so. Just run it on a tablet (I've only been able to test it on a 1st gen Nexus 7), and follow the repro steps above.