Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to use Fragment.setRetainInstance() as a replacement for Activity.onRetainNonConfigurationInstance()

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.

  1. 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.

  2. 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?

like image 281
stevesw Avatar asked Jul 21 '12 10:07

stevesw


1 Answers

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.

like image 61
antonyt Avatar answered Jan 25 '23 23:01

antonyt