Hopeless Hopeless - 3 months ago 30
Android Question

Not understanding how setRetainInstance (or RetainInstance in Xamarin Android) works?

If you have some

Fragment
declared right inside your XML layout file, then looks like
SetRetainInstance
works expectedly.

However what if the
Fragment
is loaded dynamically into some container in the
OnCreate
callback of the main
Activity
? I don't see how
setRetainInstance
means or can do in this case.

Check out this code:

protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
var f = new SomeFragment();
var ft = FragmentManager.BeginTransaction();
ft.Replace(Android.Resource.Id.Content, f);
ft.Commit();
}


You can see that everytime the
OnCreate
is called (such as when the screen is rotated), a new
SomeFragment
is created and fills the main content of the Activity.

Now even if I declare a variable (field) holding reference to the instance of fragment and create it only when it's null, something like this:

SomeFragment f;
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
if(f == null) {
f = new SomeFragment(this);
f.RetainInstance = true;
}
var ft = FragmentManager.BeginTransaction();
ft.Replace(Android.Resource.Id.Content, f);
ft.Commit();
}


This also does not work, everytime the Activity is created, the field
f
is still null initially (not retained). The only solution I found working here is declare the field
f
as static, like this:

static SomeFragment f;


With that I don't even need to use
RetainInstance
(or
setRetainInstance
in Java). In fact the requirement of loading Fragments dynamically is very popular, so at that point
RetainInstance
seems to be less helpful?

Or I missed something simple here to still take advantage of
RetainInstance
? The problem is use
RetainInstance
and dynamically load Fragments, if you have some solution or pattern to use here, please share with me, thanks!

Answer

Although I am not familiar to Xamarin, I can answer this in Java.

Instead of using

replace(int containerViewId, Fragment fragment)

You should use

replace(int containerViewId, Fragment fragment, String tag)

For example,

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment fragment;
fragment = (ExampleFragment) fragmentManager.findFragmentByTag("tag");
if(fragment == null)
    fragment = new ExampleFragment();
fragmentTransaction.replace(R.id.fragment_container, fragment, "tag");
fragmentTransaction.commit();

But you should only use setRetainInstance if you are running longe task at background. You may have a look at this blog post. Recreating the fragment is more suggested as it can avoid unexpected behavior (e.g. not initializing some variables).