Reputation: 1065
According to the Android API documentation, Activity.onRetainNonConfigurationInstance() has been deprecated in favor of Fragment.setRetainInstance().
However, I have run into two separate situations where Fragment.setRetainInstance() doesn't seem to be feasible to use.
If the Fragment contains a WebView. According to Diane Hackborne, you cannot re-use a WebView across configuration changes. Which I guess means that you need to allow the Fragment to tear-down and re-create the WebView when the screen rotates, and use WebView.saveState() and WebView.restoreState() to restore the web view state.
If the Fragment belongs to a layout that no longer exists after the configuration change, when the FragmentManager tries to restore the Fragment, it will throw:
java.lang.IllegalArgumentException: No view found for id 0x7f060091 for fragment
This can occur (for example) if you have a two-fragment layout in landscape mode, but a one-fragment layout in portrait mode. When rotating from landscape to portrait, if setRetainInstance() set to true, neither Fragment gets destroyed, but one fragment no longer has a valid view to be re-attached to, hence the exception.
So, if you're building a Fragment-based application, and you need to retain data (for example references to running AsyncTasks) between configuration changes, and you can't use Fragment.setRetainInstance(), and there is no Fragment.onRetainNonConfigurationInstance(), what is the best approach to take?
Upvotes: 13
Views: 5652
Reputation: 21883
Even if you use Fragment.setRetainInstance()
, the Fragment will still tear down its view and recreate it following a configuration change. That is, onDestroyView()
and onCreateView()
will be called in that sequence. Make sure to invalidate old references to the WebView in onDestroyView()
. You do not have to worry about re-using a WebView - a new WebView will be recreated with the correct Activity
context. This is somewhat irrelevant because for saving WebView state, you still need to call WebView.saveState(Bundle)
in Fragment.onSaveInstanceState(Bundle)
, and WebView.restoreState(Bundle)
in Fragment.onViewCreated(Bundle)
. This just means that Fragment.setRetainInstance()
is still compatible with use of WebViews.
The example you gave regarding a view container no longer existing in another orientation would mean that you do not need to retain any data - the fragment is not to be used at all after screen rotation.
In any case, instead of making a single Fragment handle both the UI and AsyncTask loading, you can separate them into two Fragments - the one with the AsyncTask does not need to be attached to the view hierarchy at all and can be set to be a retained instance. This simplifies your concerns and makes for a cleaner architecture. When the AsyncTask finishes, you need to call back to the UI fragment to deliver the results (e.g. findFragmentById/Tag, setTargetFragment, custom listeners...). The fragment managing the AsyncTask needs to be able to retrieve a reference to the new instance of the UI fragment after rotation.
Upvotes: 8
Reputation: 1065
Looks like there have been no answers to my question but here's what I ended up doing:
For Fragments containing a WebView which needs to be restored, override Fragment.onSaveInstanceState(Bundle state) and call WebView.saveState(bundle). Then in Fragment.onViewCreated(View view, Bundle savedInstanceState), call webView.restoreState(savedInstanceState) if savedInstanceState is not null.
For Fragments containing live AsyncTasks that need to be preserved, override Fragment.onDestroy() and save those tasks in my Application object. (Alternatively, stashing them in a static variable is also suggested). Then in Fragment.onCreate(Bundle savedInstanceState), check to see if those tasks are not null, and if so, restore them. (What that means is app dependent, but in my case I restore the callback to be invoked on the Fragment when the AsyncTask completes).
I'm not sure if this is the best solution but it seems to address the problems I was having.
Still, I find this whole situation rather confusing (differentiating between onSaveInstanceState(), onRetainNonConfigurationInstance(), and onDestroy(), and deciding what you need to save under each scenario).
Upvotes: 0