Apps that have been in the background for a long time will often crash when they wake up from the desktop or recent task list. This is often caused by the App being killed in the background and having problems recovering, and more often when FragmentActivity+Fragment is used. For example, if the Fragment does not provide a default constructor, it will crash when rebuilding because the reflection fails to create the Fragment. For example, if you create a new FragmentDialog in onCreate, and show, after being killed in the background, wakes up again, Will show two dialog boxes, this is why? In fact, this involves the background kill and restore mechanism, which involves FragmentActivity, ActivityManagerService, LowMemoryKiller mechanism, ActivityStack, Binder and a series of knowledge points. It might be a bit long to put in one article, so the Android Background Kill series is written in three:
- FragmentActivity and the PhoneWindow background kill mechanism
- Principle 1: ActivityManagerService and App on-site recovery mechanism (mainly describes how AMS restores the site for App)
- Principle 2: Background Killing and LowmemoryKiller(mainly about the principle of App being killed by the background)
This is the first Android background kill series, mainly explains in the development process, because the background kill involves some crashes, and how to avoid these crashes, and is a simple introduction to the onSaveInstanceState and onRestoreInstanceState execution timing and principle. These two functions are also frequently asked in Android interviews, which are slightly more detailed than the simple start mode Activity declaration cycle, and also introduce the background kill and restore principle through this point.
FragmentActivity resumes logic after it is killed by the background
When the App is killed by a background exception and you click the icon again, or when you enter from the recent task list, the system will help restore the scene and recreate the Activity. For FragmentActivity, the logic is more complicated because of Framgent. The system will rebuild the destroyed Fragment first.
Take a chestnut
When we create an Activity, create and show a DialogFragment in the onCreate function, and then somehow kill the APP (RogueKiller emulates the background kill tool), and again invoke the APP from the recent task, You will find two Dialogfragments displayed as follows:
public class DialogFragmentActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DialogFragment dialogFragment = new FragmentDlg();
dialogFragment.show(getSupportFragmentManager(), "");
}Copy the code
Not only does this make us wonder, why? The onCreate function is executed only once, but the onCreate function is executed only once. Why there are two dialogfragments? In fact, there is a DialogFragment that is reconstructed by Android’s own recovery and reconstruction mechanism. The savedInstanceState parameter of onCreate(Bundle savedInstanceState) is not null in the case of an exception kill. Instead, it contains the scene information saved when the onCreate(Bundle savedInstanceState) is killed. For a crash example, create a new CrashFragment and discard the default no-argument constructor:
public class CrashFragment extends Fragment {
public CrashFragment(String tag) {
super();
}
}Copy the code
Then Add or replace to the Activity to Add the CrashFragment. After the CrashFragment is displayed, simulate the background killing using RogueKiller simulation tool. When the App is recalled from the recent task list, it will crash.
Caused by: android.support.v4.app.Fragment$InstantiationException:
Unable to instantiate fragment xxx.CrashFragment:
make sure class name exists, is public, and has an empty constructor that is public
at android.support.v4.app.Fragment.instantiate(Fragment.java:431)
at android.support.v4.app.FragmentState.instantiate(Fragment.java:102)
at android.support.v4.app.FragmentManagerImpl.restoreAllState(FragmentManager.java:1952)
at android.support.v4.app.FragmentController.restoreAllState(FragmentController.java:144)
at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:307)
at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:81)Copy the code
Super.oncreate (savedInstanceState) does a lot of things that we don’t see when it recovers. Let’s look at crashes:
Why does a Fragment without a no-argument constructor cause a crash
Take a look at the following onCreate code for FragmentActivity in support-V4:
protected void onCreate(@Nullable Bundle savedInstanceState) {
mFragments.attachHost(null /*parent*/);
super.onCreate(savedInstanceState); . if (savedInstanceState ! =null) { Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG); mFragments.restoreAllState(p, nc ! =null ? nc.fragments : null);
}
mFragments.dispatchCreate();
}Copy the code
You can see if savedInstanceState! . = null, executes mFragments restoreAllState logic, logic, here is actually involved in recovery reconstruction was killed by the background anomaly before again, or before the Activity’s onStop execution, Both the Activity and Fragment scenes are already saved in ActivityManagerService as FragmentState. When recreating the Fragment, reflection is used to recreate the Fragment
void restoreAllState(Parcelable state, List<Fragment> nonConfig) {
...
for (int i=0; i<fms.mActive.length; i++) {
FragmentState fs = fms.mActive[i];
if(fs ! =null) { Fragment f = fs.instantiate(mHost, mParent); mActive.add(f); .Copy the code
It’s basically calling Instantiate of FragmentState, then instantiate of Fragment, and then reflection to build the Fragment, that is, the Fragment that was added to the FragmentActivity, Will be automatically created and will take the Fragment’s default no-argument constructor, otherwise it will throw a InstantiationException, which is why the crash occurred in the second example.
*/ public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) { try { Class<? > clazz = sClassMap.get(fname); if (clazz == null) { // Class not found in the cache, see if it's real, and try to add it clazz = context.getClassLoader().loadClass(fname); sClassMap.put(fname, clazz); } Fragment f = (Fragment)clazz.newInstance(); if (args ! = null) { args.setClassLoader(f.getClass().getClassLoader()); f.mArguments = args; } return f; } catch (ClassNotFoundException e) { throw new InstantiationException("Unable to instantiate fragment " + fname + ": make sure class name exists, is public, and has an" + " empty constructor that is public", e); } catch (java.lang.InstantiationException e) { throw new InstantiationException("Unable to instantiate fragment " + fname + ": make sure class name exists, is public, and has an" + " empty constructor that is public", e); } catch (IllegalAccessException e) { throw new InstantiationException("Unable to instantiate fragment " + fname + ": make sure class name exists, is public, and has an" + " empty constructor that is public", e); } } qCopy the code
The errorMsg in scene 2 corresponds to the exception thrown. The source code of the Fragment also says clearly:
/** * Default constructor. Every fragment must have an * empty constructor, so it can be instantiated when restoring its * activity's state. It is strongly recommended that subclasses do not * have other constructors with parameters, since these constructors * will not be called when the fragment is re-instantiated; instead, * arguments can be supplied by the caller with {@link #setArguments} * and later retrieved by the Fragment with {@link #getArguments}. * * Applications should generally not implement a constructor. The * first place application code an run where the fragment is ready to * be used is in {@link #onAttach(Activity)}, the point where the fragment * is actually associated with its activity. Some applications may also * want to implement {@link #onInflate} to retrieve attributes from a * layout resource, though should take care here because this happens for * the fragment is attached to its activity. */
public Fragment() {
}Copy the code
The idea is that Fragments must have an empty constructor so they can rebuild, and subclasses of fragments don’t recommend constructors with parameters. Better to hold parameters in setArguments. Let’s see why there are two Dialogfragments.
Why are there two DialogFragments
After a Fragment is created, it will not be displayed if it is not added to the Activity’s layout by adding or replacing it. When you kill DialogFragmentActivity in the background, or rotate the screen, two FragmentDialogS appear, one restored by the system and one newly created.
Add a Fragment and display the principle of the Fragment lifecycle
Normally we use the Fragment method for fragmentActivities as follows: let’s say in the onCreate function:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fragment fr = Fragment.instance("")
getSupportFragmentManager().beginTransaction()
.add(R.id.container,fr).commit();Copy the code
The getSupportFragmentManager return is a subclass of FragmentManager FragmentManagerImpl, FragmentManagerImpl FragmentActivity is an inner class, The Fragment management logic is handled by the FragmentManagerImpl. This article is based on 4.3. Later versions introduce FragmentController, which is only a layer of encapsulation.
public class FragmentActivity extends Activity{... final FragmentManagerImpl mFragments =newFragmentManagerImpl(); . final FragmentContainer mContainer =new FragmentContainer() {
@Override
@Nullable
public View findViewById(int id) {
return FragmentActivity.this.findViewById(id);
}
@Override
public boolean hasView() {
Window window = FragmentActivity.this.getWindow();
return (window! =null && window.peekDecorView() ! =null); }};Copy the code
The FragmentManagerImpl beginTransaction() function returns a BackStackRecord()
@Override
public FragmentTransaction beginTransaction() {
return new (this);
}Copy the code
As the name suggests, beginTransaction generates a Transaction for the FragmentActivity, which can either be executed or reversed, as a basis for unstacking. The FragmentTransaction add function implements the following.
public FragmentTransaction add(Fragment fragment, String tag) {
doAddOp(0, fragment, tag, OP_ADD);// Async, similar to Hander
return this;
}
private void doAddOp(int containerViewId, Fragment fragment, Stringtag, int opcmd) { fragment.mFragmentManager = mManager; . Op op =new Op();
op.cmd = opcmd;
op.fragment = fragment;
addOp(op);
}Copy the code
Commit the Transaction, insert the Transaction into the Transaction queue, and eventually call the FragmentManager addFragment method. Add FragmentManagerImpl to the FragmentManagerImpl maintenance Fragment list and adjust the Fragment to the appropriate state based on the current Activity state as follows:
public void addFragment(Fragment fragment, boolean moveToStateNow) {
if (mAdded == null) {
mAdded = new ArrayList<Fragment>();
}
makeActive(fragment);
if(! fragment.mDetached) {if (mAdded.contains(fragment)) {
throw new IllegalStateException("Fragment already added: " + fragment);
}
mAdded.add(fragment);
fragment.mAdded = true;
fragment.mRemoving = false;
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
if(moveToStateNow) { moveToState(fragment); }}}Copy the code
The FragmentManager is the core of the FragmentActivity management Fragment.
final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory {... ArrayList<Runnable> mPendingActions; Runnable[] mTmpActions; boolean mExecutingActions; ArrayList<Fragment> mActive; ArrayList<Fragment> mAdded; ArrayList<Integer> mAvailIndices; ArrayList<BackStackRecord> mBackStack;Copy the code
The FragmentManagerImpl maintains a list of all managed fragments for the FragmentActivity. The State of the FragmentManagerImpl is the same as the State of the Activity. This is the key to managing fragments. The Fragment itself does not have a life cycle. It is simply a View wrapper that relies entirely on FragmentManagerImpl to simulate the life cycle synchronously. For example, after creating the Fragment in onCreate, add, The Fragment’s onCreateView is not executed until the onCreateView of the Activity itself is executed. That is, the Fragment is passive, consistent with the FragmentActivity. Since a Fragment is just a View wrapped in a Container, how does it get converted to a View and added to a Container? The key is the moveToState function, which forces the life cycle of a newly added Fragment to be synchronized with the Activity:
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive) {
...
if (f.mState < newState) { // It is lower than the current state of the Activity
switch (f.mState) {
caseFragment.INITIALIZING: ... f.mActivity = mActivity; f.mParentFragment = mParent; f.mFragmentManager = mParent ! =null
? mParent.mChildFragmentManager : mActivity.mFragments;
f.mCalled = false; f.onAttach(mActivity); . if (! f.mRetaining) { f.performCreate(f.mSavedFragmentState); }case Fragment.CREATED:
if (newState > Fragment.CREATED) {
f.mView = f.performCreateView(f.getLayoutInflater(
f.mSavedFragmentState), container, f.mSavedFragmentState);
f.onViewCreated(f.mView, f.mSavedFragmentState);
f.performActivityCreated(f.mSavedFragmentState);
if(f.mView ! =null) {
f.restoreViewState(f.mSavedFragmentState);
}
f.mSavedFragmentState = null;
}
case Fragment.ACTIVITY_CREATED:
case Fragment.STOPPED:
if (newState > Fragment.STOPPED) {
f.performStart();
}
case Fragment.STARTED:
if (newState > Fragment.STARTED) {
f.mResumed = true;
f.performResume();Copy the code
After adding a Fragment, you need to keep the State of the Fragment consistent with the current Activity. Now, to return to the background kill state, why show two dialogfragments, we need to continue to look at the exception handling process of the Fragment, in the Fragment does not have no argument constructor will cause a crash, analysis just went to the Fragment construction. Now keep going down. After providing the no-argument constructor, the Fragment can be created correctly. After that? And then some recovery logic, and then restoreAllState
void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) {
if (state == null) return;
FragmentManagerState fms = (FragmentManagerState)state;
mActive = new ArrayList<Fragment>(fms.mActive.length);
for (int i=0; i<fms.mActive.length; i++) {
FragmentState fs = fms.mActive[i];
if(fs ! =null) {
Fragment f = fs.instantiate(mActivity, mParent);
mActive.add(f);
fs.mInstance = null;
// Build the list of currently added fragments.
if(fms.mAdded ! =null) {
mAdded = new ArrayList<Fragment>(fms.mAdded.length);
for (int i=0; i<fms.mAdded.length; i++) {
Fragment f = mActive.get(fms.mAdded[i]);
if (f == null) {
throwException(new IllegalStateException(
"No instantiated fragment for index #" + fms.mAdded[i]));
}
f.mAdded = true;
if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ":" + f);
if (mAdded.contains(f)) {
throw new IllegalStateException("Already added!");
}
mAdded.add(f);
}
// Build the back stack.
if(fms.mBackStack ! =null) {
mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
for (int i=0; i<fms.mBackStack.length; i++) {
BackStackRecord bse = fms.mBackStack[i].instantiate(this);
mBackStack.add(bse);
if (bse.mIndex >= 0) {
setBackStackIndex(bse.mIndex, bse);
}Copy the code
FragmentActiivyt’s onCreate function is used to retrieve FragmentActiivyt’s FragmentActiivyt’s onCreate function after it has been killed. By default, the Fragment has been added to the mAdded list. However, in scenario 1, we manually created a Fragment and added it to it, so there are two fragments in the mAdded function. After the FragmentActivity calls onStart, the Fragment view in the mAdded list is created, added to the corresponding container, and displayed when the Activity calls onReusume. It will show two copies, in fact, if, at this point, you kill again, restore, it will show three, kill, restart, four copies…
@Override
protected void onStart() {
super.onStart();
mStopped = false;
mReallyStopped = false;
mHandler.removeMessages(MSG_REALLY_STOPPED);
if(! mCreated) { mCreated =true;
mFragments.dispatchActivityCreated();
}
mFragments.noteStateNotSaved();
mFragments.execPendingActions();
mFragments.doLoaderStart();
// NOTE: HC onStart goes here.
mFragments.dispatchStart();
mFragments.reportLoaderStart();
}Copy the code
The above is some analysis of FramgentActivity for two scenarios, mainly dealing with Framgent when replying.
Call timing of onSaveInstanceState and OnRestoreInstance
OnSaveInstanceState is called when you click the home button or jump to another screen, but OnRestoreInstance is not called when you wake up. Why? Isn’t onSaveInstanceState paired with OnRestoreInstance? In Android, onSaveInstanceState is preprocessed to prevent the Activity from being killed by the background. If the Activity is not killed by the background, there is no need to restore the Activity on site and OnRestoreInstance will not be called. In most cases, activities are not killed that quickly.
When onSaveInstanceState is called
The onSaveInstanceState function is Android’s way of preventing an Activity from being killed in the background. The onSaveInstanceState function is executed before 2.3, before onPause, and after 2.3, before onStop. You might run out of memory and execute onSaveInstanceState when it’s reclaimed. For the startActivity function call many articles have been introduced, you can simply refer to Lao luo’s blog Android application internal startActivity process (startActivity) source code analysis, For example, when Activity A calls startActivity to startActivity B, AMS pauses Activity A, then calls B, displays B, and then stops A. When stopping A, you need to save the scene of A. An invisible Activity can be killed by the background. For example, if you open an unreserved Activity in the developer option, you can achieve this effect. When you start another Activity, the process for saving the previous Activity is as follows.
After 2.3, onSaveInstanceState is placed before onStop. Check out the FragmentActivity onSaveInstanceState source code.
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Parcelable p = mFragments.saveAllState();
if(p ! =null) { outState.putParcelable(FRAGMENTS_TAG, p); }}Copy the code
As can be seen, the first is the parent class onSaveInstanceState, mainly to save some window and View information, such as the ViewPager is currently displayed the number of views, etc.. After that, the FragmentManager’s saveAllState stores some of the state of the FragmentActivity’s own live Fragment. This data is needed for the FragmentActivity to recover from Framgent. If not handled properly, the above exception will occur.
When OnRestoreInstanceState is called
OnRestoreInstanceState is implemented in pairs with onSaveInstanceState, but onSaveInstanceState is not called in pairs. OnSaveInstanceState is always called when the Activity jumps or returns to the main screen. OnRestoreInstanceState, however, does not. It is called only when the Activity or App is killed by an exception and the recovery process takes place. OnRestoreInstanceState will not be called back if the Activity is not killed.
You can see that OnRestoreInstanceState is called after onStart, before onPostCreate. So why is normal creation not called? Take a look at the source code for starting activities in ActivityThread:
private Activity performLaunchActivity(Activi
...
mInstrumentation.callActivityOnCreate(activity, r.state);
r.activity = activity;
r.stopped = true;
if(! r.activity.mFinished) { activity.performStart(); r.stopped =false;
}
if(! r.activity.mFinished) {if(r.state ! =null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); }}if(! r.activity.mFinished) { activity.mCalled =false; mInstrumentation.callActivityOnPostCreate(activity, r.state); }}Copy the code
As you can see, only R. state! . = null, only through mInstrumentation callActivityOnRestoreInstanceState callback OnRestoreInstanceState, R. state is the data that ActivityManagerService passes to ActivityThread through Binder for scenario recovery. So that’s some analysis of when onSaveInstanceState and OnRestoreInstance execute. The following combined with the specific system View control to analyze the specific application of these two functions: ViewPager and FragmentTabHost, for example, are two of the most commonly used controls on the main screen, and are internally compatible with background killing, which is why the ViewPager can automatically locate the last view after being killed.
ViewPager is compatible with background killing
First of all, ViewPager is compatible. Even if the ViewPager is killed in the background, it can still be restored to the last closed position. This is also an optimization of the experience. In the previous analysis of onSaveInstanceState and onRestoreInstanceState, we only focused on the Fragment processing. In fact, there are some processing for Windows and VIES. Let’s take a look at what onSaveInstanceState saves for the window:
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
}Copy the code
PhonwWinow.java
@Override
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
if (mContentParent == null) {
return outState;
}
SparseArray<Parcelable> states = new SparseArray<Parcelable>();
mContentParent.saveHierarchyState(states);
outState.putSparseParcelableArray(VIEWS_TAG, states);
// save the focused view idView focusedView = mContentParent.findFocus(); . outState.putInt(FOCUSED_ID_TAG, focusedView.getId());// save the panels
if (panelStates.size() > 0) {
outState.putSparseParcelableArray(PANELS_TAG, panelStates);
}
if(mActionBar ! =null) {
outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
}
return outState;
}Copy the code
A Window is PhonwWinow, and saveHierarchyState is a collection of scene information for the View in the current Window, such as: The ID of the View currently getting focus, the ActionBar, some state of the View, and of course the saveHierarchyState recursively traverses all child Views, saving all the states that need to be saved:
ViewGroup.java
@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
super.dispatchSaveInstanceState(container);
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
View c = children[i];
if((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) ! = PARENT_SAVE_DISABLED) { c.dispatchSaveInstanceState(container); }}}Copy the code
Visible, the function first by super. DispatchSaveInstanceState save their state, and recursion is passed to the View. OnSaveInstanceState is used to retrieve the State that the View needs to save and store its own ID as a Key in the SparseArray States list, which is actually a list of PhoneWindows. Binder saves this data to ActivityManagerService
View.java
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
if(mID ! = NO_ID && (mViewFlags & SAVE_DISABLED_MASK) ==0) {
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
Parcelable state = onSaveInstanceState();
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onSaveInstanceState()");
}
if(state ! =null) { container.put(mID, state); }}}Copy the code
So what information is stored against the ViewPager? It is easy to see from the following code that a new SavedState scenario data is created and the current location mCurItem is stored in it.
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.position = mCurItem;
if(mAdapter ! =null) {
ss.adapterState = mAdapter.saveState();
}
return ss;
}Copy the code
So that’s pretty much what I’m doing here. So let’s look at the restoration of the ViewPager and what the onRestoreInstanceState actually does,
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if(mWindow ! =null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if(windowState ! =null) { mWindow.restoreHierarchyState(windowState); }}}Copy the code
Can be seen from the code, it is to get at that time, the window of the saved information, through the mWindow. RestoreHierarchyState do data recovery,
@Override
public void restoreHierarchyState(Bundle savedInstanceState) {
if (mContentParent == null) {
return;
}
SparseArray<Parcelable> savedStates
= savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
if(savedStates ! =null) {
mContentParent.restoreHierarchyState(savedStates);
}
...
if (mActionBar != null) {... mActionBar.restoreHierarchyState(actionBarStates); }}Copy the code
What happens to ViewPager? It is easy to see from the source code, in fact, is to retrieve the SavedState, and obtain the location of the exception killed, for subsequent recovery,
ViewPager.java
@Override
public void onRestoreInstanceState(Parcelable state) {
if(! (stateinstanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState)state;
super.onRestoreInstanceState(ss.getSuperState());
if(mAdapter ! =null) {
mAdapter.restoreState(ss.adapterState, ss.loader);
setCurrentItemInternal(ss.position, false.true);
} else{ mRestoredCurItem = ss.position; mRestoredAdapterState = ss.adapterState; mRestoredClassLoader = ss.loader; }}Copy the code
This explains how ViewPager saves and restores the scene through onSaveInstanceState and onRestoreInstanceState. If you use ViewPager+FragmentAdapter, you’re involved in both the recovery of the FragmentActivity and the recovery of the ViewPager, and the FragmentAdapter is also compatible with background killing. To prevent repeated creation of fragments, see the source code for FragmentAdapter:
FragmentPagerAdapter.java
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);
// Do we already have this fragment?<! -- Have you created a Fragment? -->String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
1Do not create a Fragment using getItem if the corresponding Tag exists in the Activityif(fragment ! =null) {
mCurTransaction.attach(fragment);
} else {
2If there is no corresponding Fragment in the Activity, create a Fragment = getItem(position) using getItem. mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId)); }if(fragment ! = mCurrentPrimaryItem) { FragmentCompat.setMenuVisibility(fragment,false);
FragmentCompat.setUserVisibleHint(fragment, false);
}
return fragment;
}Copy the code
The onCreate function of the FragmentActivity rebuilds the Fragment list. Those fragments are not recreated using getItem. Look at the FragmentTabHost control. FragmentTabHost is also a common home page control. FragmentTabHost also has a background kill mechanism. As the name suggests, this control was created specifically for fragments.
FragmentTabHost is compatible with background killing
FragmentTabHost is similar to ViewPager in that the current position is saved on onSaveInstanceState and postion is restored on onRestoreInstanceState and reassigned to Tabhost. Later, when FragmentTabHost is onAttachedToWindow, it can set the current position based on the restored postion as follows:
FragmentTabHost.java
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.curTab = getCurrentTabTag();
return ss;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if(! (stateinstanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setCurrentTabByTag(ss.curTab);
}Copy the code
When onAttachedToWindow is executed on FragmentTabHost, it will first getCurrentTabTag, which is the value of the SavedState that was recovered if it was killed in the background. After that, use doTabChanged to switch to the responsive Tab. Note that the Fragment will not be created again because it has been rebuilt.
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
StringcurrentTab = getCurrentTabTag(); . ft = doTabChanged(currentTab, ft);if(ft ! =null) { ft.commit(); mFragmentManager.executePendingTransactions(); }}Copy the code
App development for background kill processing
- The simplest way, but not so effective: cancel system restore
For example: For FragmentActivity, do not rebuild:
protected void onCreate(Bundle savedInstanceState) {
if(savedInstanceState ! =null) {savedInstanceState. PutParcelable (" android: support: fragments could ",null); }super.onCreate(savedInstanceState);
} Copy the code
If the Actvity is “Android: Fragments”, please note that ViewPager and FragmentTabHost do not need to be processed.
For Windows, if you don’t want the View to use recovery logic, override the onRestoreInstanceState function in the FragmentActivity of the base class.
protected void onRestoreInstanceState(Bundle savedInstanceState) {
}Copy the code
Of course, all the above methods are rude. It is better to follow the design of Android, save the site where it needs to be saved, and restore the site where it needs to be restored by removing the corresponding data. This is the analysis of background kill for FragmentActivity, onSaveInstanceState, onRestoreInstanceState. And how ActivityManagerService handles kill and restore.
Android background Kill (FragmentActivity, PhoneWindow) LowMemoryKiller (4.3-6.0) LowMemoryKiller (4.3-6.0) Binder Obituary Principle Android Background Kill series 5: Android process alive – “cut” or rogue
For reference only, welcome to correct mistakes
Reference documentation
Fragment Transactions & Activity State Loss Lowmemorykiller Fragment instantiation, Fragment$InstantiationException android.app.Fragment$InstantiationException Android Low Memory Square: Get rid of your Fragments today! ActivityStackSupervisor analyzes A Deeper Look of tasks Analysis of onSaveInstanceState and onRestoreInstanceState processes of ViewPager and FragmentStatePagerAdaper View