Android后台杀死系列之一:FragmentActivity及PhoneWindow后台杀死处理机制

简介:

App在后台久置后,再次从桌面或最近的任务列表唤醒时经常会发生崩溃,这往往是App在后台被系统杀死,再次恢复的时候遇到了问题,而在使用FragmentActivity+Fragment的时候会更加频繁。比如,如果Fragment没有提供默认构造方法,就会在重建的时候因为反射创建Fragment失败而崩溃,再比如,在onCreate里面new 一个FragmentDialog,并且show,被后台杀死后,再次唤醒的时候,就会show两个对话框,这是为什么?其实这就涉及了后台杀死及恢复的机制,其中涉及的知识点主要是FragmentActivity、ActivityManagerService、LowMemoryKiller机制、ActivityStack、Binder等一系列知识点。放在一篇文章里面可能会有些长,因此,Android后台杀死系列写了三篇:

  • 开篇:FragmentActivity及PhoneWindow后台杀死处理机制
  • 原理篇1:后台杀死与LowmemoryKiller(主要讲述App被后台杀死的原理)
  • 原理篇2:后台杀死与App现场恢复(主要讲述AMS如何为App恢复现场的原理)

本篇是Android后台杀死系列的第一篇,主要讲解在开发过程中,由于后台杀死涉及的一些崩溃,以及如何避免这些崩溃,还有就是简单的介绍一下onSaveInstanceState与onRestoreInstanceState执行时机与原理,这两个函数也是Android面试时常问的两个点,是比简单的启动模式Activity声明周期稍微更深入细致一些的地方,也通过这个点引入后台杀死及恢复原理。

FragmentActivity被后台杀死后恢复逻辑

当App被后台异常杀死后,再次点击icon,或者从最近任务列表进入的时候,系统会帮助恢复当时的场景,重新创建Activity,对于FragmentActivity,由于其中有Framgent,逻辑会相对再复杂一些,系统会首先重建被销毁的Fragment。

举个栗子

我们创建一个Activity,并且在onCreate函数中新建并show一个DialogFragment,之后通过某种方式将APP异常杀死(RogueKiller模拟后台杀死工具),再次从最近的任务唤起App的时候,会发现显示了两个DialogFragment,代码如下:

 
 
  1. public class DialogFragmentActivity extends AppCompatActivity { 
  2.  
  3.     @Override 
  4.     protected void onCreate(Bundle savedInstanceState) { 
  5.         super.onCreate(savedInstanceState); 
  6.         DialogFragment dialogFragment = new FragmentDlg(); 
  7.         dialogFragment.show(getSupportFragmentManager(), ""); 
  8.     }  

这不仅让我们奇怪,为什么呢?虽然被杀死了,但是onCreate函数在执行的时候还是只执行了一次啊,为什么会出现两个DialogFragment,这里其实就有一个DialogFragment是通过Android自身的恢复重建机制重建出来,在异常杀死的情况下onCreate(Bundle savedInstanceState)函数的savedInstanceState参数也不是null,而是包含了被杀死时所保存的场景信息。再来看个崩溃的例子,新建一个CrashFragment,并且丢弃默认无参构造方法:

 
 
  1. public class CrashFragment extends Fragment { 
  2.  
  3.     public CrashFragment(String tag) { 
  4.         super(); 
  5.     } 
  6.  

之后再Activity中Add或replace添加这个CrashFragment,在CrashFragment显示后,通过RogueKiller模拟后台杀死工具模拟后台杀死,再次从最近任务列表里唤起App的时候,就会遇到崩溃,

 
 
  1. Caused by: android.support.v4.app.Fragment$InstantiationException:  
  2.   Unable to instantiate fragment xxx.CrashFragment:  
  3.   make sure class name exists, is publicand has an empty constructor that is public 
  4.         at android.support.v4.app.Fragment.instantiate(Fragment.java:431) 
  5.         at android.support.v4.app.FragmentState.instantiate(Fragment.java:102) 
  6.         at android.support.v4.app.FragmentManagerImpl.restoreAllState(FragmentManager.java:1952) 
  7.         at android.support.v4.app.FragmentController.restoreAllState(FragmentController.java:144) 
  8.         at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:307) 
  9.         at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:81)  

上面的这两个问题主要涉及后台杀死后FragmentActivity自身的恢复机制,其实super.onCreate(savedInstanceState)在恢复时做了很多我们没有看到的事情,先看一下崩溃:

为什么Fragment没有无参构造方法会引发崩溃

看一下support-V4中FragmentActivity中onCreate代码如下:

 
 
  1. protected void onCreate(@Nullable Bundle savedInstanceState) { 
  2.     mFragments.attachHost(null /*parent*/); 
  3.  
  4.     super.onCreate(savedInstanceState); 
  5.                     ... 
  6.     if (savedInstanceState != null) { 
  7.         Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG); 
  8.         mFragments.restoreAllState(p, nc != null ? nc.fragments : null); 
  9.     } 
  10.     mFragments.dispatchCreate(); 
  11.  

可以看到如果savedInstanceState != null,就会执行mFragments.restoreAllState逻辑,其实这里就牵扯到恢复时重建逻辑,再被后台异常杀死前,或者说在Activity的onStop执行前,Activity的现场以及Fragment的现场都是已经被保存过的,其实是被保存早ActivityManagerService中,保存的格式FragmentState,重建的时候,会采用反射机制重新创Fragment

 
 
  1. void restoreAllState(Parcelable state, List<Fragment> nonConfig) { 
  2.   
  3.       ... 
  4.          for (int i=0; i<fms.mActive.length; i++) { 
  5.         FragmentState fs = fms.mActive[i]; 
  6.         if (fs != null) { 
  7.             Fragment f = fs.instantiate(mHost, mParent); 
  8.             mActive.add(f); 
  9.     ...  

其实就是调用FragmentState的instantiate,进而调用Fragment的instantiate,最后通过反射,构建Fragment,也就是,被加到FragmentActivity的Fragment在恢复的时候,会被自动创建,并且采用Fragment的默认无参构造方法,如果没哟这个方法,就会抛出InstantiationException异常,这也是为什么第二个例子中会出现崩溃的原因。

 
 
  1. public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) { 
  2.     try { 
  3.         Class<?> clazz = sClassMap.get(fname); 
  4.         if (clazz == null) { 
  5.             // Class not found in the cache, see if it's realand try to add it 
  6.             clazz = context.getClassLoader().loadClass(fname); 
  7.             sClassMap.put(fname, clazz); 
  8.         } 
  9.         Fragment f = (Fragment)clazz.newInstance(); 
  10.         if (args != null) { 
  11.             args.setClassLoader(f.getClass().getClassLoader()); 
  12.             f.mArguments = args; 
  13.         } 
  14.         return f; 
  15.     } catch (ClassNotFoundException e) { 
  16.         throw new InstantiationException("Unable to instantiate fragment " + fname 
  17.                 + ": make sure class name exists, is public, and has an" 
  18.                 + " empty constructor that is public", e); 
  19.     } catch (java.lang.InstantiationException e) { 
  20.         throw new InstantiationException("Unable to instantiate fragment " + fname 
  21.                 + ": make sure class name exists, is public, and has an" 
  22.                 + " empty constructor that is public", e); 
  23.     } catch (IllegalAccessException e) { 
  24.         throw new InstantiationException("Unable to instantiate fragment " + fname 
  25.                 + ": make sure class name exists, is public, and has an" 
  26.                 + " empty constructor that is public", e); 
  27.     } 
  28.  

可以看到场景二提示的errormsg跟抛出的异常是可以对应上的,其实Fragment源码里面也说得很清楚:

 
 
  1.  /** 
  2.  * Default constructor.  <strong>Every</strong> fragment must have an 
  3.  * empty constructor, so it can be instantiated when restoring its 
  4.  * activity's state.  It is strongly recommended that subclasses do not 
  5.  * have other constructors with parameters, since these constructors 
  6.  * will not be called when the fragment is re-instantiated; instead
  7.  * arguments can be supplied by the caller with {@link #setArguments} 
  8.  * and later retrieved by the Fragment with {@link #getArguments}. 
  9.  *  
  10.  * <p>Applications should generally not implement a constructor.  The 
  11.  * first place application code an run where the fragment is ready to 
  12.  * be used is in {@link #onAttach(Activity)}, the point where the fragment 
  13.  * is actually associated with its activity.  Some applications may also 
  14.  * want to implement {@link #onInflate} to retrieve attributes from a 
  15.  * layout resource, though should take care here because this happens for 
  16.  * the fragment is attached to its activity. 
  17.  */ 
  18.   
  19. public Fragment() { 
  20.  

大意就是,Fragment必须有一个空构造方法,这样才能保证重建流程,并且,Fragment的子类也不推荐有带参数的构造方法,最好采用setArguments来保存参数。下面再来看下为什么会出现两个DialogFragment。

为什么出现两个DialogFragment

Fragment在被创建之后,如果不通过add或者replace添加到Activity的布局中是不会显示的,在保存现场的时候,也是保存了add的这个状态的,来看一下Fragment的add逻辑:此时被后台杀死,或旋转屏幕,被恢复的DialogFragmentActivity时会出现两个FragmentDialog,一个被系统恢复的,一个新建的。

Add一个Fragment,并显示的原理--所谓Fragment生命周期

通常我们FragmentActivity使用Fragment的方法如下:假设是在oncreate函数中:

 
 
  1. @Override 
  2. protected void onCreate(Bundle savedInstanceState) { 
  3.         super.onCreate(savedInstanceState); 
  4.         Fragment fr = Fragment.instance(""
  5.         getSupportFragmentManager().beginTransaction() 
  6.         .add(R.id.container,fr).commit();  

其中getSupportFragmentManager返回的是FragmentManager的子类FragmentManagerImpl,FragmentManagerImpl是FragmentActivity的一个内部类,其Fragment的管理逻辑都是由FragmentManagerImpl来处理的,本文是基于4.3,后面的高版本引入了FragmentController其实也只是多了一层封装,原理差别不是太大,有兴趣可以自己分析:

 
 
  1. public class FragmentActivity extends Activity{ 
  2.     ... 
  3.     final FragmentManagerImpl mFragments = new FragmentManagerImpl(); 
  4.    ... 
  5.     final FragmentContainer mContainer = new FragmentContainer() { 
  6.         @Override 
  7.         @Nullable 
  8.         public View findViewById(int id) { 
  9.             return FragmentActivity.this.findViewById(id); 
  10.         } 
  11.  
  12.         @Override 
  13.         public boolean hasView() { 
  14.             Window window = FragmentActivity.this.getWindow(); 
  15.             return (window != null && window.peekDecorView() != null); 
  16.         } 
  17.     };  

FragmentManagerImpl的beginTransaction()函数返回的是一个BackStackRecord()

 
 
  1. @Override 
  2. public FragmentTransaction beginTransaction() { 
  3.     return new (this); 

从名字就可以看出,beginTransaction是为FragmentActivity生成一条Transaction(事务),可以执行,也可以反向,作为退栈的一个依据,FragmentTransaction的add函数实现如下,

 
 
  1. public FragmentTransaction add(Fragment fragment, String tag) { 
  2.     doAddOp(0, fragment, tag, OP_ADD);//异步操作的,跟Hander类似 
  3.     return this; 
  4.  
 
 
  1. private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) { 
  2.     fragment.mFragmentManager = mManager; 
  3.      ... 
  4.     Op op = new Op(); 
  5.     op.cmd = opcmd; 
  6.     op.fragment = fragment; 
  7.     addOp(op); 
  8.  

之后commit这个Transaction, 将Transaction插入到Transaction队列中去,最终会回调FragmentManager的addFragment方法,将Fragment添加FragmentManagerImpl到维护Fragment列表中去,并且根据当前的Activity状态,将Fragment调整到合适的状态,代码如下:

 
 
  1. public void addFragment(Fragment fragment, boolean moveToStateNow) { 
  2.  
  3.     if (mAdded == null) { 
  4.         mAdded = new ArrayList<Fragment>(); 
  5.     } 
  6.  
  7.     makeActive(fragment); 
  8.      
  9.     if (!fragment.mDetached) { 
  10.         if (mAdded.contains(fragment)) { 
  11.             throw new IllegalStateException("Fragment already added: " + fragment); 
  12.         } 
  13.         mAdded.add(fragment); 
  14.         fragment.mAdded = true
  15.         fragment.mRemoving = false
  16.         if (fragment.mHasMenu && fragment.mMenuVisible) { 
  17.             mNeedMenuInvalidate = true
  18.         } 
  19.         if (moveToStateNow) { 
  20.             moveToState(fragment); 
  21.         } 
  22.     } 
  23. }    

为什么说FragmentManager是FragmentActivity管理Fragment的核心呢,请看下面:

 
 
  1. final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory { 
  2.     ... 
  3.      
  4.     ArrayList<Runnable> mPendingActions; 
  5.     Runnable[] mTmpActions; 
  6.     boolean mExecutingActions; 
  7.      
  8.     ArrayList<Fragment> mActive; 
  9.     ArrayList<Fragment> mAdded; 
  10.     ArrayList<Integer> mAvailIndices; 
  11.     ArrayList<BackStackRecord> mBackStack; 
  12.    

可以看出FragmentManagerImpl帮FragmentActivity维护着所有管理Fragment的列表,FragmentManagerImpl的State是和Activity的State一致的,这是管理Fragment的关键。其实Fragment自身是没有什么生命周期的,它只是一个View的封装,完全依靠FragmentManagerImpl来进行同步模拟生命周期,比如在onCreate函数中创建Fragment,add后,在执行的到Activity自身的onCreateView之前,Fragment的onCreateView是不会执行的,也就是Fragment是被动式的跟FragmentActivity保持一致。既然Fragment只是个View的封装,那么它是如何转换成View,并添加到Container中去的呢?关键是moveToState函数,这个函数强制将新add的Fragment的生命周期与Activity同步:

 
 
  1. void moveToState(Fragment f, int newState, int transit, int transitionStyle, 
  2.         boolean keepActive) { 
  3.         ...         
  4.      if (f.mState < newState) { //低于当前Activity的状态 
  5.         switch (f.mState) { 
  6.             case Fragment.INITIALIZING: 
  7.                     ... 
  8.                 f.mActivity = mActivity; 
  9.                 f.mParentFragment = mParent; 
  10.                 f.mFragmentManager = mParent != null 
  11.                         ? mParent.mChildFragmentManager : mActivity.mFragments; 
  12.                 f.mCalled = false
  13.                 f.onAttach(mActivity); 
  14.                ... 
  15.                 if (!f.mRetaining) { 
  16.                     f.performCreate(f.mSavedFragmentState); 
  17.                 }  
  18.             case Fragment.CREATED: 
  19.                 if (newState > Fragment.CREATED) { 
  20.              
  21.                       f.mView = f.performCreateView(f.getLayoutInflater( 
  22.                       f.mSavedFragmentState), container, f.mSavedFragmentState); 
  23.                       f.onViewCreated(f.mView, f.mSavedFragmentState); 
  24.                   
  25.                     f.performActivityCreated(f.mSavedFragmentState); 
  26.                     if (f.mView != null) { 
  27.                         f.restoreViewState(f.mSavedFragmentState); 
  28.                     } 
  29.                     f.mSavedFragmentState = null
  30.                 } 
  31.             case Fragment.ACTIVITY_CREATED: 
  32.             case Fragment.STOPPED: 
  33.                     if (newState > Fragment.STOPPED) { 
  34.                         f.performStart(); 
  35.                     } 
  36.             case Fragment.STARTED: 
  37.                 if (newState > Fragment.STARTED) { 
  38.                       f.mResumed = true
  39.                     f.performResume();  

可以看出,add Fragment之后,需要让Fragment跟当前Activity的State保持一致。现在回归正题,对于后台杀死状态下,为什么会show两个DialogFragment呢,我们需要接着看就要Fragment的异常处理的流程,在Fragment没有无参构造方法会引发崩溃里面,分析只是走到了Fragment的构建,现在接着往下走。提供无参构造函数后,Fragment可以正确的新建出来,之后呢?之后就是一些恢复逻辑,接着看restoreAllState

 
 
  1. void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) { 
  2.  
  3.     if (state == nullreturn
  4.     FragmentManagerState fms = (FragmentManagerState)state; 
  5.     mActive = new ArrayList<Fragment>(fms.mActive.length); 
  6.      for (int i=0; i<fms.mActive.length; i++) { 
  7.         FragmentState fs = fms.mActive[i]; 
  8.         if (fs != null) { 
  9.             Fragment f = fs.instantiate(mActivity, mParent); 
  10.  
  11.             mActive.add(f); 
  12.             fs.mInstance = null
  13.  
  14.     // Build the list of currently added fragments. 
  15.     if (fms.mAdded != null) { 
  16.         mAdded = new ArrayList<Fragment>(fms.mAdded.length); 
  17.         for (int i=0; i<fms.mAdded.length; i++) { 
  18.             Fragment f = mActive.get(fms.mAdded[i]); 
  19.             if (f == null) { 
  20.                 throwException(new IllegalStateException( 
  21.                         "No instantiated fragment for index #" + fms.mAdded[i])); 
  22.             } 
  23.             f.mAdded = true
  24.             if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f); 
  25.             if (mAdded.contains(f)) { 
  26.                 throw new IllegalStateException("Already added!"); 
  27.             } 
  28.             mAdded.add(f); 
  29.         } 
  30.      
  31.     // Build the back stack. 
  32.     if (fms.mBackStack != null) { 
  33.         mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length); 
  34.         for (int i=0; i<fms.mBackStack.length; i++) { 
  35.             BackStackRecord bse = fms.mBackStack[i].instantiate(this); 
  36.  
  37.             mBackStack.add(bse); 
  38.             if (bse.mIndex >= 0) { 
  39.                 setBackStackIndex(bse.mIndex, bse); 
  40.  

其实到现在现在Fragment相关的信息已经恢复成功了,之后随着FragmentActivity周期显示或者更新了,这些都是被杀死后,在FragmentActiivyt的onCreate函数处理的,也就是默认已经将之前的Fragment添加到mAdded列表中去了,但是,在场景一,我们有手动新建了一个Fragment,并添加进去,所以,mAdded函数中就有连个两个Fragment。这样,在FragmentActivity调用onStart函数之后,会新建mAdded列表中Fragment的视图,将其添加到相应的container中去,并在Activity调用onReusume的时候,显示出来做的,这个时候,就会显示两份,其实如果,在这个时候,你再杀死一次,恢复,就会显示三分,在杀死,重启,就是四份。。。。

 
 
  1. @Override 
  2. protected void onStart() { 
  3.     super.onStart(); 
  4.  
  5.     mStopped = false
  6.     mReallyStopped = false
  7.     mHandler.removeMessages(MSG_REALLY_STOPPED); 
  8.  
  9.     if (!mCreated) { 
  10.         mCreated = true
  11.         mFragments.dispatchActivityCreated(); 
  12.     } 
  13.  
  14.     mFragments.noteStateNotSaved(); 
  15.     mFragments.execPendingActions(); 
  16.  
  17.     mFragments.doLoaderStart(); 
  18.  
  19.     // NOTE: HC onStart goes here. 
  20.  
  21.     mFragments.dispatchStart(); 
  22.     mFragments.reportLoaderStart(); 
  23.  

以上就是针对两个场景,对FramgentActivity的一些分析,主要是回复时候,对于Framgent的一些处理。

onSaveInstanceState与OnRestoreInstance的调用时机

在在点击home键,或者跳转其他界面的时候,都会回调用onSaveInstanceState,但是再次唤醒却不一定调用OnRestoreInstance,这是为什么呢?onSaveInstanceState与OnRestoreInstance难道不是配对使用的?在Android中,onSaveInstanceState是为了预防Activity被后台杀死的情况做的预处理,如果Activity没有被后台杀死,那么自然也就不需要进行现场的恢复,也就不会调用OnRestoreInstance,而大多数情况下,Activity不会那么快被杀死。

onSaveInstanceState的调用时机

onSaveInstanceState函数是Android针对可能被后台杀死的Activity做的一种预防,它的执行时机在2.3之前是在onPause之前,2.3之后,放在了onStop函数之前,也就说Activity失去焦点后,可能会由于内存不足,被回收的情况下,都会去执行onSaveInstanceState。对于startActivity函数的调用很多文章都有介绍,可以简单参考下老罗的博客Android应用程序内部启动Activity过程(startActivity)的源代码分析,比如在Activity A 调用startActivity启动Activity B的时候,会首先通过AMS pause Activity A,之后唤起B,在B显示,再stop A,在stop A的时候,需要保存A的现场,因为不可见的Activity都是可能被后台杀死的,比如,在开发者选项中打开不保留活动,就会达到这种效果,在启动另一个Activity时,上一个Activity的保存流程大概如下,这里先简单描述,在下一篇原理篇的时候,会详细讲解下流程:

在2.3之后,onSaveInstanceState的时机都放在了onStop之前,看一下FragmentActivity的onSaveInstanceState源码:

 
 
  1. @Override 
  2. protected void onSaveInstanceState(Bundle outState) { 
  3.     super.onSaveInstanceState(outState); 
  4.     Parcelable p = mFragments.saveAllState(); 
  5.     if (p != null) { 
  6.         outState.putParcelable(FRAGMENTS_TAG, p); 
  7.     } 
  8.  

可以看出,首先就是父类的onSaveInstanceState,主要是保存一些窗口及View的信息,比如ViewPager当前显示的是第几个View等。之后,就是就是通过FragmentManager的saveAllState,来保存FragmentActivity自身的现场-Fragment的一些状态,这些数据是FragmentActivity恢复Framgent所必须的数据,处理不好就会出现上面的那种异常。

OnRestoreInstanceState的调用时机

之前已经说过,OnRestoreInstanceState虽然与onSaveInstanceState是配对实现的,但是其调用却并非完全成对的,在Activity跳转或者返回主界面时,onSaveInstanceState是一定会调用的,但是OnRestoreInstanceState却不会,它只有Activity或者App被异常杀死,走恢复流程的时候才会被调用。如果没有被异常杀死,不走Activity的恢复新建流程,也就不会回调OnRestoreInstanceState,简单看一下Activity的加载流程图:

可以看出,OnRestoreInstanceState的调用时机是在onStart之后,在onPostCreate之前。那么正常的创建为什么没调用呢?看一下ActivityThread中启动Activity的源码:

 
 
  1. private Activity performLaunchActivity(Activi 
  2.          
  3.         ... 
  4.          mInstrumentation.callActivityOnCreate(activity, r.state); 
  5.                  
  6.               r.activity = activity; 
  7.               r.stopped = true
  8.               if (!r.activity.mFinished) { 
  9.                   activity.performStart(); 
  10.                   r.stopped = false
  11.               } 
  12.               if (!r.activity.mFinished) { 
  13.                   if (r.state != null) { 
  14.                       mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); 
  15.                   } 
  16.               } 
  17.               if (!r.activity.mFinished) { 
  18.                   activity.mCalled = false
  19.                   mInstrumentation.callActivityOnPostCreate(activity, r.state); 
  20.             
  21.               } 
  22.     }  

可以看出,只有r.state != null的时候,才通过mInstrumentation.callActivityOnRestoreInstanceState回调OnRestoreInstanceState,r.state就是ActivityManagerService通过Binder传给ActivityThread数据,主要用来做场景恢复。以上就是onSaveInstanceState与OnRestoreInstance执行时机的一些分析。下面结合具体的系统View控件来分析一下这两个函数的具体应用:比如ViewPager与FragmentTabHost,这两个空间是主界面最常用的控件,内部对后台杀死做了兼容,这也是为什么被杀死后,Viewpager在恢复后,能自动定位到上次浏览的位置。

ViewPager应对后台杀死做的兼容

首先看一下ViewPager做的兼容,ViewPager在后台杀死的情况下,仍然能恢复到上次关闭的位置,这也是对体验的一种优化,这其中的原理是什么?之前分析onSaveInstanceState与onRestoreInstanceState的时候,只关注了Fragment的处理,其实还有一些针对Window窗口及Vie的处理,先看一下onSaveInstanceState针对窗口保存了什么:

 
 
  1. protected void onSaveInstanceState(Bundle outState) { 
  2.     outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState()); 
  3.   }  

PhonwWinow.java

 
 
  1. @Override 
  2. public Bundle saveHierarchyState() { 
  3.     Bundle outState = new Bundle(); 
  4.     if (mContentParent == null) { 
  5.         return outState; 
  6.     } 
  7.      
  8.     SparseArray<Parcelable> states = new SparseArray<Parcelable>(); 
  9.     mContentParent.saveHierarchyState(states); 
  10.     outState.putSparseParcelableArray(VIEWS_TAG, states); 
  11.  
  12.     // save the focused view id 
  13.       View focusedView = mContentParent.findFocus(); 
  14.       ... 
  15.       outState.putInt(FOCUSED_ID_TAG, focusedView.getId()); 
  16.     // save the panels 
  17.     if (panelStates.size() > 0) { 
  18.         outState.putSparseParcelableArray(PANELS_TAG, panelStates); 
  19.     } 
  20.     if (mActionBar != null) { 
  21.         outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates); 
  22.     } 
  23.  
  24.     return outState; 
  25.  

Window其实就是PhonwWinow,saveHierarchyState其实就是针对当前窗口中的View保存一些场景信息 ,比如:当前获取焦点的View的id、ActionBar、View的一些状态,当然saveHierarchyState递归遍历所有子View,保存所有需要保存的状态:

ViewGroup.java

 
 
  1. @Override 
  2. protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) { 
  3.     super.dispatchSaveInstanceState(container); 
  4.     final int count = mChildrenCount; 
  5.     final View[] children = mChildren; 
  6.     for (int i = 0; i < count; i++) { 
  7.         View c = children[i]; 
  8.         if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) { 
  9.             c.dispatchSaveInstanceState(container); 
  10.         } 
  11.     } 
  12.  

可见,该函数首先通过super.dispatchSaveInstanceState保存自身的状态,再递归传递给子View。onSaveInstanceState主要用于获取View需要保存的State,并将自身的ID作为Key,存储到SparseArray<Parcelable> states列表中,其实就PhoneWindow的一个列表,这些数据最后会通过Binder保存到ActivityManagerService中去

View.java

 
 
  1. protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) { 
  2.     if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) { 
  3.         mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED; 
  4.         Parcelable state = onSaveInstanceState(); 
  5.         if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) { 
  6.             throw new IllegalStateException( 
  7.                     "Derived class did not call super.onSaveInstanceState()"); 
  8.         } 
  9.         if (state != null) { 
  10.             container.put(mID, state); 
  11.         } 
  12.     } 
  13.  

那么针对ViewPager到底存储了什么信息?通过下面的代码很容易看出,其实就是新建个了一个SavedState场景数据,并且将当前的位置mCurItem存进去。

 
 
  1. @Override 
  2. ublic Parcelable onSaveInstanceState() { 
  3.    Parcelable superState = super.onSaveInstanceState(); 
  4.    SavedState ss = new SavedState(superState); 
  5.    ss.position = mCurItem; 
  6.    if (mAdapter != null) { 
  7.        ss.adapterState = mAdapter.saveState(); 
  8.    } 
  9.    return ss;  

到这里存储的事情基本就完成了。接下来看一下ViewPager的恢复以及onRestoreInstanceState到底做了什么,

 
 
  1. protected void onRestoreInstanceState(Bundle savedInstanceState) { 
  2.     if (mWindow != null) { 
  3.         Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG); 
  4.         if (windowState != null) { 
  5.             mWindow.restoreHierarchyState(windowState); 
  6.         } 
  7.     } 
  8.  

从代码可以看出,其实就是获取当时保存的窗口信息,之后通过mWindow.restoreHierarchyState做数据恢复,

 
 
  1. @Override 
  2. public void restoreHierarchyState(Bundle savedInstanceState) { 
  3.     if (mContentParent == null) { 
  4.         return
  5.     } 
  6.  
  7.     SparseArray<Parcelable> savedStates 
  8.             = savedInstanceState.getSparseParcelableArray(VIEWS_TAG); 
  9.     if (savedStates != null) { 
  10.         mContentParent.restoreHierarchyState(savedStates); 
  11.     } 
  12.     ... 
  13.      
  14.     if (mActionBar != null) { 
  15.         ... 
  16.           mActionBar.restoreHierarchyState(actionBarStates); 
  17.       } 
  18.  

对于ViewPager会发生什么?从源码很容易看出,其实就是取出SavedState,并获取到异常杀死的时候的位置,以便后续的恢复,

ViewPager.java

 
 
  1. @Override 
  2. public void onRestoreInstanceState(Parcelable state) { 
  3.     if (!(state instanceof SavedState)) { 
  4.         super.onRestoreInstanceState(state); 
  5.         return
  6.     } 
  7.  
  8.     SavedState ss = (SavedState)state; 
  9.     super.onRestoreInstanceState(ss.getSuperState()); 
  10.  
  11.     if (mAdapter != null) { 
  12.         mAdapter.restoreState(ss.adapterState, ss.loader); 
  13.         setCurrentItemInternal(ss.position, falsetrue); 
  14.     } else { 
  15.         mRestoredCurItem = ss.position; 
  16.         mRestoredAdapterState = ss.adapterState; 
  17.         mRestoredClassLoader = ss.loader; 
  18.     } 
  19. }   

以上就解释了ViewPager是如何通过onSaveInstanceState与onRestoreInstanceState保存、恢复现场的。如果是ViewPager+FragmentAdapter的使用方式,就同时涉及FragmentActivity的恢复、也牵扯到Viewpager的恢复,其实FragmentAdapter也同样针对后台杀死做了一些兼容,防止重复新建Fragment,看一下FragmentAdapter的源码:

FragmentPagerAdapter.java

 
 
  1. @Override 
  2. public Object instantiateItem(ViewGroup container, int position) { 
  3.     if (mCurTransaction == null) { 
  4.         mCurTransaction = mFragmentManager.beginTransaction(); 
  5.     } 
  6.  
  7.     final long itemId = getItemId(position); 
  8.  
  9.     // Do we already have this fragment? 
  10.     <!--是否已经新建了Fragment??--> 
  11.      
  12.     String name = makeFragmentName(container.getId(), itemId); 
  13.     Fragment fragment = mFragmentManager.findFragmentByTag(name); 
  14.      
  15.     1 如果Activity中存在相应Tag的Fragment,就不要通过getItem新建 
  16.      
  17.     if (fragment != null) { 
  18.         mCurTransaction.attach(fragment); 
  19.     } else { 
  20.     2 如果Activity中不存在相应Tag的Fragment,就需要通过getItem新建 
  21.         fragment = getItem(position); 
  22.         mCurTransaction.add(container.getId(), fragment, 
  23.                 makeFragmentName(container.getId(), itemId)); 
  24.     } 
  25.     if (fragment != mCurrentPrimaryItem) { 
  26.         FragmentCompat.setMenuVisibility(fragment, false); 
  27.         FragmentCompat.setUserVisibleHint(fragment, false); 
  28.     } 
  29.  
  30.     return fragment; 
  31.  

从1与2 可以看出,通过后台恢复,在FragmentActivity的onCreate函数中,会重建Fragment列表,那些被重建的Fragment不会再次通过getItem再次创建,再来看一下相似的控件FragmentTabHost,FragmentTabHost也是主页常用的控件,FragmentTabHost也有相应的后台杀死处理机制,从名字就能看出,这个是专门针对Fragment才创建出来的控件。

FragmentTabHost应对后台杀死做的兼容

FragmentTabHost其实跟ViewPager很相似,在onSaveInstanceState执行的时候保存当前位置,并在onRestoreInstanceState恢复postion,并重新赋值给Tabhost,之后FragmentTabHost在onAttachedToWindow时,就可以根据恢复的postion设置当前位置,代码如下:

FragmentTabHost.java

 
 
  1. @Override 
  2. protected Parcelable onSaveInstanceState() { 
  3.     Parcelable superState = super.onSaveInstanceState(); 
  4.     SavedState ss = new SavedState(superState); 
  5.     ss.curTab = getCurrentTabTag(); 
  6.     return ss; 
  7.  
  8. @Override 
  9. protected void onRestoreInstanceState(Parcelable state) { 
  10.     if (!(state instanceof SavedState)) { 
  11.         super.onRestoreInstanceState(state); 
  12.         return
  13.     } 
  14.     SavedState ss = (SavedState) state; 
  15.     super.onRestoreInstanceState(ss.getSuperState()); 
  16.     setCurrentTabByTag(ss.curTab); 
  17.  

在FragmentTabHost执行onAttachedToWindow时候,会首先getCurrentTabTag ,如果是经历了后台杀死,这里得到的值其实是恢复的SavedState里的值,之后通过doTabChanged切换到响应的Tab,注意这里切换的时候,Fragment由于已经重建了,是不会再次新建的。

 
 
  1. @Override 
  2. protected void onAttachedToWindow() { 
  3.     super.onAttachedToWindow(); 
  4.  
  5.     String currentTab = getCurrentTabTag(); 
  6.     ... 
  7.      
  8.     ft = doTabChanged(currentTab, ft); 
  9.      
  10.     if (ft != null) { 
  11.         ft.commit(); 
  12.         mFragmentManager.executePendingTransactions(); 
  13.     } 
  14.  

App开发时针对后台杀死处理方式

最简单的方式,但是效果一般:取消系统恢复

比如:针对FragmentActivity ,不重建:

 
 
  1. protected void onCreate(Bundle savedInstanceState) { 
  2.      if (savedInstanceState != null) { 
  3.      savedInstanceState.putParcelable(“android:support:fragments”, null);} 
  4.      super.onCreate(savedInstanceState); 
  5. }    

如果是系统的Actvity改成是“android:fragments",不过这里需要注意:对于ViewPager跟FragmentTabHost不需要额外处理,处理了可能反而有反作用。

针对Window,如果不想让View使用恢复逻辑,在基类的FragmentActivity中覆盖onRestoreInstanceState函数即可。

 
 
  1. protected void onRestoreInstanceState(Bundle savedInstanceState) { 
  2.  

当然以上的做法都是比较粗暴的做法,最好还是顺着Android的设计,在需要保存现场的地方保存,在需要恢复的地方,去除相应的数据进行恢复。以上就是后台杀死针对FragmentActivity、onSaveInstanceState、onRestoreInstanceState的一些分析,后面会有两篇针对后台杀死原理,以及ActivityManagerService如何处理杀死及恢复的文章。




作者:happysnail
来源:51CTO
目录
相关文章
“framework必会”系列:Android Input系统(一)事件读取机制
曾经在开发的很长一段时间内,笔者对点击事件的认知只存在于自定义View中的`onTouchEvent`等方法的处理。 后来慢慢的接触到`Android的事件分发机制`,但也只是在**Activity->ViewGroup->View**层面的分发逻辑
|
4月前
|
缓存 算法 Java
Linux内核新特性年终大盘点-安卓杀后台现象减少的背后功臣MGLRU算法简介
MGLRU是一种新型内存管理算法,它的出现是为了弥补传统LRU(Least Recently Used)和LFU(Least Frequently Used)算法在缓存替换选择上的不足,LRU和LFU的共同缺点就是在做内存页面替换时,只考虑内存页面在最近一段时间内被访问的次数和最后一次的访问时间,但是一个页面的最近访问次数少或者最近一次的访问时间较早,可能仅仅是因为这个内存页面新近才被创建,属于刚刚完成初始化的年代代页面,它的频繁访问往往会出现在初始化之后的一段时间里,那么这时候就把这种年轻代的页面迁移出去
|
4月前
|
Android开发 容器
[Android]View的事件分发机制(源码解析)
[Android]View的事件分发机制(源码解析)
36 0
|
4月前
|
消息中间件 缓存 安全
android开发,使用kotlin学习消息机制Handler
android开发,使用kotlin学习消息机制Handler
84 0
|
4月前
|
安全 Android开发 Kotlin
android开发,使用kotlin学习Android权限机制
android开发,使用kotlin学习Android权限机制
41 0
|
4月前
|
存储 移动开发 JavaScript
【原生】sd.js帮助您简化繁重的获取数据、存储数据(CRUD)骚操作(吐槽~在安卓9.0以下或者IOS10.X以下手机端H5页面不支持,在这两种情况下的系统只能使用ajax或者原生js请求后台数据)
【原生】sd.js帮助您简化繁重的获取数据、存储数据(CRUD)骚操作(吐槽~在安卓9.0以下或者IOS10.X以下手机端H5页面不支持,在这两种情况下的系统只能使用ajax或者原生js请求后台数据)
|
11月前
|
Android开发
使用WakeLock使Android应用程序保持后台唤醒
使用WakeLock使Android应用程序保持后台唤醒
193 0
|
8月前
|
Android开发
Android JetPack组件之ViewModel状态的保存(程序在后台被系统杀死数据也存活)
Android JetPack组件之ViewModel状态的保存(程序在后台被系统杀死数据也存活)
100 0
|
8月前
|
Android开发
Android 使用Alarm机制创建长时间在后台运行的定时任务
Android 使用Alarm机制创建长时间在后台运行的定时任务
158 0
|
8月前
|
Java API Android开发
Android 权限机制详解
Android 权限机制详解
74 0