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

简介: Android后台杀死系列之一:FragmentActivity及PhoneWindow后台杀死处理机制(二)

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的保存流程大概如下,这里先简单描述,在下一篇原理篇的时候,会详细讲解下流程:


image.png

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


@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Parcelable p = mFragments.saveAllState();
    if (p != null) {
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
}

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


OnRestoreInstanceState的调用时机


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

image.png

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

 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);
               }
     }

可以看出,只有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针对窗口保存了什么:

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

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 id
      View 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;
}

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

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);
        }
    }
}

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

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

  @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;
}

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

protected void onRestoreInstanceState(Bundle savedInstanceState) {
    if (mWindow != null) {
        Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
        if (windowState != null) {
            mWindow.restoreHierarchyState(windowState);
        }
    }
}

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

@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);
      }
}

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

ViewPager.java

@Override
public void onRestoreInstanceState(Parcelable state) {
    if (!(state instanceof 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;
    }
} 

以上就解释了ViewPager是如何通过onSaveInstanceState与onRestoreInstanceState保存、恢复现场的。如果是ViewPager+FragmentAdapter的使用方式,就同时涉及FragmentActivity的恢复、也牵扯到Viewpager的恢复,其实FragmentAdapter也同样针对后台杀死做了一些兼容,防止重复新建Fragment,看一下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?
    <!--是否已经新建了Fragment??-->
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    1 如果Activity中存在相应Tag的Fragment,就不要通过getItem新建
    if (fragment != null) {
        mCurTransaction.attach(fragment);
    } else {
    2 如果Activity中不存在相应Tag的Fragment,就需要通过getItem新建
        fragment = getItem(position);
        mCurTransaction.add(container.getId(), fragment,
                makeFragmentName(container.getId(), itemId));
    }
    if (fragment != mCurrentPrimaryItem) {
        FragmentCompat.setMenuVisibility(fragment, false);
        FragmentCompat.setUserVisibleHint(fragment, false);
    }
    return fragment;
}

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


image.png


FragmentTabHost应对后台杀死做的兼容


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


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 (!(state instanceof SavedState)) {
        super.onRestoreInstanceState(state);
        return;
    }
    SavedState ss = (SavedState) state;
    super.onRestoreInstanceState(ss.getSuperState());
    setCurrentTabByTag(ss.curTab);
}

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

@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    String currentTab = getCurrentTabTag();
    ...
    ft = doTabChanged(currentTab, ft);
    if (ft != null) {
        ft.commit();
        mFragmentManager.executePendingTransactions();
    }
}


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


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


比如:针对FragmentActivity ,不重建:

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

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


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

protected void onRestoreInstanceState(Bundle savedInstanceState) {
}

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


目录
相关文章
|
消息中间件 Android开发
Android Handler的使用方式以及其机制的简单介绍
Handler 是 Android 中实现线程间通信的重要机制,可传递任意两线程数据。常用场景包括子线程向主线程(UI 线程)传递结果,以及主线程向子线程发送消息。其核心涉及四个类:Handler(发送/接收消息)、Message(消息载体)、MessageQueue(消息队列)和 Looper(消息循环泵)。基本流程为:Handler 发送 Message 至 MessageQueue,Looper 从队列中按 FIFO 取出并处理。
456 0
|
Linux Android开发 iOS开发
深入探索Android与iOS的多任务处理机制
在移动操作系统领域,Android和iOS各有千秋,尤其在多任务处理上展现出不同的设计理念和技术实现。本文将深入剖析两大平台在后台管理、资源分配及用户体验方面的策略差异,揭示它们如何平衡性能与电池寿命,为用户带来流畅而高效的操作体验。通过对比分析,我们不仅能够更好地理解各自系统的工作机制,还能为开发者优化应用提供参考。
|
算法 Linux 调度
深入探索安卓系统的多任务处理机制
【10月更文挑战第21天】 本文旨在为读者提供一个关于Android系统多任务处理机制的全面解析。我们将从Android操作系统的核心架构出发,探讨其如何管理多个应用程序的同时运行,包括进程调度、内存管理和电量优化等方面。通过深入分析,本文揭示了Android在处理多任务时所面临的挑战以及它如何通过创新的解决方案来提高用户体验和设备性能。
1059 1
|
存储 安全 Android开发
探索Android与iOS的隐私保护机制
在数字化时代,移动设备已成为我们生活的一部分,而隐私安全是用户最为关注的问题之一。本文将深入探讨Android和iOS两大主流操作系统在隐私保护方面的策略和实现方式,分析它们各自的优势和不足,以及如何更好地保护用户的隐私。
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
494 1
|
存储 安全 数据安全/隐私保护
探索安卓与iOS的隐私保护机制####
【10月更文挑战第15天】 本文深入剖析了安卓和iOS两大操作系统在隐私保护方面的策略与技术实现,旨在揭示两者如何通过不同的技术手段来保障用户数据的安全与隐私。文章将逐一探讨各自的隐私控制功能、加密措施以及用户权限管理,为读者提供一个全面而深入的理解。 ####
1163 1
|
消息中间件 存储 Java
Android消息处理机制(Handler+Looper+Message+MessageQueue)
Android消息处理机制(Handler+Looper+Message+MessageQueue)
637 2
|
消息中间件 存储 Java
Android消息处理机制(Handler+Looper+Message+MessageQueue)
Android消息处理机制(Handler+Looper+Message+MessageQueue)
344 2
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
548 8

热门文章

最新文章