Last active
August 1, 2016 07:48
-
-
Save jjhesk/f1d1263852d48f06ed3e072a32247bb6 to your computer and use it in GitHub Desktop.
memory management for the leak fix for on LILIPOP before.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class NestingFragment extends Fragment{ | |
//As we setRetainInstanceState(true), this field will hold the reference of //the old ChildFragmentManager | |
private FragmentManager mRetainedChildFragmentManager; | |
private FragmentManager getMyChildFragmentManager() { | |
if(mRetainedChildFragmentManager == null) { | |
//Hold the reference of child fragment manager created by //Fragment internally and hold it for the future recreated //fragment since Fragment#getChildFragmentManager() always //creates new fragment manager after rev 20. | |
mRetainedChildFragmentManager = getChildFragmentManager(); | |
} | |
return mRetainedChildFragmentManager; | |
} | |
@Override | |
public void onAttach(Activity activity) { | |
super.onAttach(activity); | |
if(mChildFragmentManager != null) { | |
//restore the last retained child fragment manager to the new | |
//created fragment | |
try { | |
Field childFMField = Fragment.class.getDeclaredField("mChildFragmentManager"); | |
childFMField.setAccessible(true); | |
childFMField.set(this, mChildFragmentManager); | |
} catch (NoSuchFieldException e) { | |
e.printStackTrace(); | |
} catch (IllegalAccessException e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
// Other code ... | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class NestingFragment extends Fragment { | |
//...other codes | |
private FragmentManager retainedChildFragmentManager; | |
private FragmentHostCallback currentHost; | |
private Class fragmentImplClass; | |
private Field mHostField; | |
{ | |
//Prepare the reflections to manage hiden fileds | |
try { | |
fragmentImplClass = Class.forName("android.support.v4.app.FragmentManagerImpl"); | |
mHostField = fragmentImplClass.getDeclaredField("mHost"); | |
mHostField.setAccessible(true); | |
} catch (ClassNotFoundException e) { | |
throw new RuntimeException("FragmentManagerImpl is renamed due to the " + | |
"change of Android SDK, this workaround doesn't work any more. " + | |
"See the issue at " + | |
"https://code.google.com/p/android/issues/detail?id=74222", e); | |
} catch (NoSuchFieldException e) { | |
throw new RuntimeException("FragmentManagerImpl.mHost is found due to the " + | |
"change of Android SDK, this workaround doesn't work any more. " + | |
"See the issue at " + | |
"https://code.google.com/p/android/issues/detail?id=74222", e); | |
} | |
} | |
@Override | |
public void onCreate(Bundle savedInstanceState){ | |
super.onCreate(savedInstanceState); | |
setRetainInstance(true); | |
} | |
protected FragmentManager childFragmentManager() { | |
if (retainedChildFragmentManager == null) { | |
retainedChildFragmentManager = getChildFragmentManager(); | |
} | |
return retainedChildFragmentManager; | |
} | |
@Override | |
public void onAttach(Context context) { | |
super.onAttach(context); | |
if (retainedChildFragmentManager != null) { | |
//restore the last retained child fragment manager to the new | |
//created fragment | |
try { | |
//Copy the mHost(Activity) to retainedChildFragmentManager | |
currentHost = (FragmentHostCallback) mHostField.get(getFragmentManager()); | |
Field childFMField = Fragment.class.getDeclaredField("mChildFragmentManager"); | |
childFMField.setAccessible(true); | |
childFMField.set(this, retainedChildFragmentManager); | |
refreshHosts(getFragmentManager()); | |
} catch (Exception e) { | |
logger.warn(e.getMessage(), e); | |
} | |
//Refresh children fragment's hosts | |
} else { | |
//If the child fragment manager has not been retained yet, let it hold the internal | |
//child fragment manager as early as possible. This can prevent child fragment | |
//manager from missing to be set and then retained, which could happen when | |
//OS kills activity and restarts it. In this case, the delegate fragment restored | |
//but childFragmentManager() may not be called so mRetainedChildFragmentManager is | |
//yet set. If the fragment is rotated, the state of child fragment manager will be | |
//lost since mRetainedChildFragmentManager hasn't set to be retained by the OS. | |
retainedChildFragmentManager = getChildFragmentManager(); | |
} | |
} | |
private void refreshHosts(FragmentManager fragmentManager) throws IllegalAccessException { | |
if (fragmentManager != null) { | |
replaceFragmentManagerHost(fragmentManager); | |
} | |
//replace host(activity) of fragments already added | |
List<fragment> frags = fragmentManager.getFragments(); | |
if (frags != null) { | |
for (Fragment f : frags) { | |
if (f != null) { | |
try { | |
//Copy the mHost(Activity) to retainedChildFragmentManager | |
Field mHostField = Fragment.class.getDeclaredField("mHost"); | |
mHostField.setAccessible(true); | |
mHostField.set(f, currentHost); | |
} catch (Exception e) { | |
logger.warn(e.getMessage(), e); | |
} | |
if (f.getChildFragmentManager() != null) { | |
refreshHosts(f.getChildFragmentManager()); | |
} | |
} | |
} | |
} | |
} | |
//replace host(activity) of the fragment manager so that new fragments it creates will be attached | |
//with correct activity | |
private void replaceFragmentManagerHost(FragmentManager fragmentManager) throws IllegalAccessException { | |
if (currentHost != null) { | |
mHostField.set(fragmentManager, currentHost); | |
} | |
} | |
//...other codes | |
} |
Suggested revision:
try {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1 && this.getChildFragmentManager() != null) {
Field childFMField = Fragment.class.getDeclaredField("mChildFragmentManager");
childFMField.setAccessible(true);
childFMField.set(this, null);
}
} catch (NoSuchFieldException ex) {
Log.e(TAG, ex.getMessage(), ex);
} catch (IllegalAccessException ex) {
Log.e(TAG, ex.getMessage(), ex);
}
This doesn't work any more on API 24
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is another version