With the recent announcement of Android N, Google added Multi-Window functionality to the OS. The implementation looks good from a user’s perspective, but here at WillowTree, we wanted to know what this meant for our apps. So we set up a simple test to see which lifecycle events are triggered and how that might affect our development process. Here’s the gist , but to summarize, we basically just put log statements in the onCreate/onResume/onPause methods.
Here’s the video of us manipulating the activity: Embedded content: https://www.youtube.com/embed/xV9Ow95VstU
And here are the logs:
DIALOG_SPLITSCREEN:onCreate()com.willowtreeapps.dialogsplitscreentest.MainActivity@6dff2fc DIALOG_SPLITSCREEN: onResume() DIALOG_SPLITSCREEN: onPause() DIALOG_SPLITSCREEN:onCreate()com.willowtreeapps.dialogsplitscreentest.MainActivity 24ab39 DIALOG_SPLITSCREEN: onResume() DIALOG_SPLITSCREEN: onPause() DIALOG_SPLITSCREEN:onCreate()com.willowtreeapps.dialogsplitscreentest.MainActivity@662e1e2 DIALOG_SPLITSCREEN: onResume() DIALOG_SPLITSCREEN: onPause()
As you can see, the activity is destroyed and re-created while you’re dragging your finger to resize the windows. This means that any code you have in onCreate/onPause/onResume is going to happen a ton while users resize their apps. This has pretty big implications depending on how you architected your apps. So how do you handle these changes? First check and make sure you have the correct windowBackground attribute specified. If your activity takes too long to draw itself then the OS will use that color to fill in the window appropriately. Second, make sure you’re loading data in a non-activity lifecycle dependent way. Google has been pushing Loaders in the past few weeks pretty hard and we can now see why. With loaders, the data is loaded and then returned intelligently to the current activity. That means that if you trigger a loader and then do the dragging exercise we performed here, the data would be loaded, kept in memory, and delivered to each newly created activity. This prevents data thrashing that might occur with other solutions. In addition, as you leave activities and start new ones, the data loaded will automatically be cleaned up when the app or device needs more memory. Lastly, if you need something that’s dependent on the lifecycle, check out the new callbacks such as Activity.onMultiWindowChanged(), Activity.onPictureInPictureChanged(),and react appropriately. (Time to update that lifecycle chart!) Note that you can also query the state of the activity with Activity.inMultiWindow() and Activity.inPictureInPicture(). The values of the inMultiWindow are a little interesting (at least for the moment) when these callbacks are called. Here are the logs for when you enter multi-window mode:
onCreate() inMultiWindow() == true onResume() inMultiWindow() == true onPause() onMultiWindowChanged() inMultiWindow() == true onResume() inMultiWindow() == true
As you can see here, the activity recreates itself, and then pauses itself, calls onMultiWindowChanged() and then resumes itself. All this despite the fact that inMultiWindow() returns true throughout that series of events. As you drag, onMultiWindowChanged() is no longer called, but here are the events when leaving the mode:
onCreate() inMultiWindow() == true onResume() inMultiWindow() == true onMultiWindowChanged() inMultiWindow() == false
It’s too soon to know if this is a bug or not, but as you can see here, exiting multi-window mode has a different order of lifecycle callbacks. Not only that, but the onCreate and onResume methods still think the activity is in multi-window mode. I can’t confirm it at this time, but it is my suspicion that the activity was at its full size when transitioning from multi-window mode. This means that perhaps the OS determined that the activity did not have to be recreated. So there you have it! Much of this could change as the preview progresses, but it seems that multi-window mode will present some challenges. It’s nothing you can’t work around with proper testing and architecture though!