`
砺雪凝霜
  • 浏览: 156348 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论
阅读更多

Activity生命周期异常分析 

我们知道正常情况下Activity创建的时候会执行 onCreate onStart onResume 方法;当锁屏情况下Activity会执行 onPause onStop方法;当屏幕再次显示的时候会执行onReStart onStart onResume方法。但是在一些特殊情况下,如语言切换,横竖屏切换等配置改变以及内存吃紧的情况下,activity就有可能被杀死,从而导致生命周期异常。这对于开发人员来说,了解这些是很有必要的。下面我们来具体分析这两种情况吧

 

(一)情况1:资源相关的配置信息发生改变导致Activity生命周期异常

 

理解这种情况,首先我们要对系统的资源加载机制有一定了解,这里简单说明一下,就拿string资源文件来说,当我们的项目要做国际化的时候,系统会根据当前的local来加载不同的string资源文件(设定了不同国家的string文件),还有当横屏手机和竖屏手机会拿到不同的图片(设定了landscape或者portrait状态下的图片),上面这两种配置发生改变的情况下,Activity默认会被销毁并创建。

 

 异常情况下Activity生命周期异常分析

当系统配置发生改变后,Activity会被销毁,其onPause,onStop,onDestroy方法均会被调用,同时由于Activity是在异常情况下终止运行的,此时系统会调用onSaveInstanceState来保存当前Activity的状态。同时在恢复时Activity运行时,又会调用onRestoreInstanceState方法来恢复Activity的状态。

    同时我们也要知道,当Activity在异常终止然后又重新启动创建时,系统默认会为我们在onSaveInstanceState方法中用Bundle保存当前Activity一些状态,如文本框输入的数据,ListView滚动的位置,然后又会在onCreate方法之前调用onRestoreInstanceState恢复这些状态。

 

1.1 Activity状态恢复机制

至于这些View的状态是怎么保存的,你在看View源码的时候,你会发现在View的源码中onSaveInstance和onRestoreInstanceState方法,Activity中也有这2个方法,难道Activity通过调用View的这2个方法来恢复状态的?(说明:Fragment其实也有这2个方法)

 

       答案是肯定的,关于保存和恢复View的层级结构,系统的工作流程是这样的:首先Activity被意外终止时,Activity会调用onSaveInstance方法去保存数据,然后Activity会委托Window去保存数据,接着Window再委托它上面的顶层容器去保存数据,顶层容器(DecorView)是一个ViewGroup.最后顶层容器再去一一通知它的子元素来保存数据,这样整个保存过程就这样完成了。可以发现,这是一种典型的委托思想,上层委托下层,容器委托子元素去处理一件事情,这种思想在Android中有很多应用,比如View的绘制过程,事件分发过程都是采用类似的思想。(说明:fragment跟View的情况也是类似的)

 

 1.2 Activity中View状态恢复机制,源码分析

 也就是说,Activity调用onSaveInstanceState方法恢复数据的同时,View也会执行onSaveInstance方法保存数据,同时调用onRestoreInstanceState时View也会调用onRestoreInstanceState恢复之前的状态。我们来看看源码来分析,分析吧

(1) Activity的onSaveInstanceState方法源码

  

protected void onSaveInstanceState(Bundle outState) {
        outState.putBundle("android:viewHierarchyState",   this.mWindow.saveHierarchyState());
        Parcelable p = this.mFragments.saveAllState();
        if(p != null) {
            outState.putParcelable("android:fragments", p);
        }
 
        this.getApplication().dispatchActivitySaveInstanceState(this,     outState);
}           
 

 

    可以看出onSaveInstanceInstanceState中,window会调用saveHierarchyState,同时会通过Buddle把所有Fragment的数据放在key值为” android:fragments”的对象中,如果是FragmentActivity则key为“android:support:fragments”

 

     (2)下面看看Window的saveHierarchyState方法源码

    我们都知道Activity是挂在一个Window下面的,用AS发现Window的saveHierarchyState方法是一个抽象方法,但是又无法查看到其子类PhoneWindow的源码。最后通过用everyThing在Android源码中,找到了PhoneWindow中该方法的源码。

 

/** {@inheritDoc} */
    @Override
    public void restoreHierarchyState(Bundle savedInstanceState) {
        if (mContentParent == null) {
            return;
        }
 
        SparseArray<Parcelable> savedStates
                = savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
        if (savedStates != null) {
            mContentParent.restoreHierarchyState(savedStates);
        }
 
        // restore the focused view
        int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID);
        if (focusedViewId != View.NO_ID) {
            View needsFocus = mContentParent.findViewById(focusedViewId);
            if (needsFocus != null) {
                needsFocus.requestFocus();
            } else {
                Log.w(TAG,
                        "Previously focused view reported id " + focusedViewId
                                + " during save, but can't be found during restore.");
            }
        }
     
    }

 

 

     你会发现saveHierarchyState其实质上执行了mContentParent.restoreHierarchyState(savedStates)方法,mContentParent其实是一个ViewGroup,restoreHierarchyState到底干了写什么呢?在View找到了该方法,其实调用了dispatchRestoreInstanceState,最终调用了View的  onRestoreInstanceState方法。

 

protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
        if(this.mID != -1) {
            Parcelable state = (Parcelable)container.get(this.mID);
            if(state != null) {
                this.mPrivateFlags &= -131073;
                this.onRestoreInstanceState(state);
                if((this.mPrivateFlags & 131072) == 0) {
                    throw new IllegalStateException("Derived class did not call   super.onRestoreInstanceState()");
                }
            }
        }
 
    }

 

   ViewGroup又对父类View的dispatchRestoreInstanceState方法进行了重载,源码如下:

 

   protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
        super.dispatchRestoreInstanceState(container);
        int count = this.mChildrenCount;
        View[] children = this.mChildren;
 
        for(int i = 0; i < count; ++i) {
            View c = children[i];
            if((c.mViewFlags & 536870912) != 536870912) {
                c.dispatchRestoreInstanceState(container);
            }
        }
 
    }

 

 

明显,ViewGroup会一一通知每个子View让其去执行dispatchRestoreInstanceState保存各自的状态。

 

    总结:ActivityonRestoreInstanceState方法也一样的。通过上面的源码分析可以得出结论,Activity通过异常终止时,会保存Fragment的状态,同时也会委托Window,让DecorView去保存Activity中所有View的状态。

 

1.3 Fragment是如何保存自己状态的

 

 在做TV项目的时候,当项目启动了一个由多个Fragment组成的Activity时,语言从简体中文切换成英文后,此时再次启动APP,结果发现Fragment中莫名报了View为空的一个空指针的异常。经过长时间的爬坑,通过google搜索也没有很好的解决方法,最后通过查看源码终于解决了该问题,现在就来分析分析该问题是如何解决的。

   很明显该问题是由于配置的改变(多语言切换),导致Activity异常终止又重新创建。此时:

Activity的生命周期是:onResume onStop onDestroy onSaveInstanceState onCreate onStart onResume。   Fragment的生命周期 :onResume onStop onDestroyView onCreateView

 

     我觉得肯定是由于Fragment部分数据保存造成的。所以我把Activity的onSaveInstanceState方法屏蔽了,结果就异常就解决了,当时很开心,问题解决了,但是这样做不好啊,Activity里的数据就都不能保存了。能不能只屏蔽Fragment中的数据呢?答案是可以的。

 

     继续看Activity 的onSaveInstanceState方法源码,里面有一段这样的代码

 

Parcelable p = this.mFragments.saveAllState();
        if(p != null) {
            outState.putParcelable("android:fragments", p);
        }
 }

 

 

      可以看出Activity拿到mFragment保存的数据,然后Activity把数据放在Buddle中,其中Key为"android:fragments",这就好办了,Buddle其实就是一个集合类,只要在Activity中的onSaveInstanceState方法中通过Buddle remove调该key值就不让Fragment保留之前的状态吗?通过实践,这种方法确实可行,但是要注意对于Activity来说该key值为"android:fragments",但是FragmentActivity又变成了“android:support:fragments”了。

 

最后我贴出关键代码:

(1)Activity中onSaveInstanceState方法,其中pageIndex为Activity选中的fragment对应的index值,并移除fragment保存的状态。

 

  @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if(outState != null){
            outState.putInt("pageIndex",mJumpType);
            outState.remove(this.getFragmentTagForSaveInstance());
        }
    }

 

(2) getFragmentTagForSaveInstance()方法是拿到保存fragment状态时对应的key值。这里是通过反射来获取的,代码如下:

  protected String getFragmentTagForSaveInstance() {
        try {
            Field f = Activity.class.getDeclaredField("FRAGMENTS_TAG");
            f.setAccessible(true);
            Object fragmentTagObj = f.get(null);
            if (fragmentTagObj != null) {
                return String.valueOf(fragmentTagObj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "android:fragments";
    }

 

 (3)Activty onCreate方法拿到key值为pageIndex的值

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(savedInstanceState != null){
            mJumpType = savedInstanceState.getInt("pageIndex",0);
        }
   }  

  

    说明:由于Activity周期异常导致,Fragment空指针的异常是google系统级的bug,该问题在stackFlow上也没有很好的解决办法,希望这篇文章对大家分析Activity和fragment生命周期异常的情况有所帮助。

 

(二)情况2:资源内存不足导致低优先级的Activity被杀死

   这种情况不好模拟,但是其数据存储和恢复过程情况和情况一完全一致。这里描述一下Activity的优先级情况。Activity按照优先级从高到低,可以分为如下三种:

(1) 前台Activity       - 正在和用户交互的Activity,优先级级最高

 

(2)可见单非前台Activity - 比如Activity弹出了一个对话框,导致Activit 可见,但是位于后台无法和用户进行交互

 

(3)后台Activity  -  已经被暂停的Activity,比如执行了onStop方法,优先级最低。

 

当系统内存不足时,系统就会按照上述优先级去杀死目前Activity所在的进程,并在后续通过onSaveInstanceState和onRestoreInstanceState来存储和恢复数据。如果一个进程中没有四大组件在执行,那么这个进程很容易就被杀死。比较好的方法是后台工作放在Service中从而保证进程有一定的优先级,这样就不会轻易地被系统杀死。

 

 

 

 

分享到:
评论

相关推荐

    Android开发 - Activity生命周期

    源码分析是深入理解Activity生命周期的重要途径。通过阅读和分析Android系统的源代码,开发者可以了解系统如何调用Activity的生命周期方法,以及如何处理各种情况。同时,这也为自定义行为提供了基础,比如创建...

    Android应用源码之(Activity生命周期).zip

    这个压缩包“Android应用源码之(Activity生命周期).zip”显然是为了帮助开发者更直观地理解和学习Activity生命周期的各个阶段。 Activity的生命周期主要由七个关键回调方法构成: 1. **onCreate()**:当Activity...

    Android应用源码之8.Activity生命周期.zip

    "Android应用源码之8.Activity生命周期.zip"这个压缩包很可能是包含了一个或多个示例项目,用于演示和解释Activity在不同情况下的生命周期状态变化。 Activity的生命周期主要分为以下几个关键状态: 1. **创建...

    Fragment的生命周期以及使用技巧源码

    本文将深入探讨Fragment的生命周期及其使用技巧,同时提供源码分析。 ### 1. Fragment的生命周期 Fragment拥有自己的生命周期,它与Activity紧密关联,但并不完全相同。Fragment的主要生命周期方法包括: - `...

    activity插件

    它通常会提供诸如智能跳转、状态保存、异常处理等功能,帮助开发者更好地管理和控制Activity的生命周期。 该插件的工作流程可能包括以下几个方面: 1. **生命周期管理**:自动跟踪和管理Activity的onCreate、...

    Android高级应用源码-从注册流程 分析如何安全退出多个Activity 多种方式.zip

    1. **Activity生命周期**:每个Activity都有其特定的生命周期,包括onCreate(), onStart(), onResume(), onPause(), onStop(), onDestroy()等方法。理解这些方法的调用顺序及条件对于确保Activity的正常运行至关重要...

    Activity 启动过程源码+分析过程

    - **减少启动时间**:合理设计Activity的生命周期,避免不必要的操作在`onCreate()`中执行,尽可能延迟加载。 - **内存管理**:及时释放资源,避免内存泄漏,提高系统运行效率。 - **Intent过滤器优化**:确保...

    FragmentActivity源码下载

    为了支持Fragment,`FragmentActivity`重新实现了许多生命周期方法,如`onCreate()`、`onStart()`、`onResume()`等。这些方法在Activity的生命周期中被调用时,也会相应地通知到相关的Fragment实例。 ##### 4.3 ...

    DefaultActivity

    开发者需要掌握Activity的生命周期、用户界面设计、以及与其他组件的协作,才能构建出功能完备且用户体验优秀的应用。通过研究压缩包中的文件,我们可以学习到具体的实现细节,进一步提升Android开发技能。

    android activity启动模式详解

    理解并灵活运用这些启动模式,可以帮助开发者更好地控制Activity的生命周期,提高应用性能,避免出现异常情况,如内存泄漏或用户界面混乱。在设计应用程序时,务必根据实际需求选择合适的启动模式,以提供最佳用户...

    Android高级应用源码-模拟Activity进出栈.rar

    - 每个Activity都有一个完整的生命周期,包括`onCreate()`, `onStart()`, `onResume()`, `onPause()`, `onStop()`, `onDestroy()`等关键回调方法。 - 进栈时,Activity会依次调用`onCreate()`, `onStart()`, `...

    安卓启动退出网络判断相关-从注册流程分析如何安全退出多个Activity多种方式.rar

    总之,这份压缩包文件涵盖了Android应用开发中的重要知识点,包括Activity生命周期管理、网络状态判断、广播接收器的使用以及多Activity管理策略。开发者可以通过研究这些代码和文档,提升自己在安卓应用开发中的...

    ActivityLifeCycle:一个Android项目,可在日志中显示活动生命周期

    Activity生命周期是每个Android开发者必须深入了解的关键概念,因为它直接影响到应用的性能、内存管理和用户体验。`ActivityLifeCycle`项目就是一个很好的学习资源,它直观地展示了Activity如何在日志中记录其各个...

    Android-Application被回收引发空指针异常分析(消灭全局变量

    在Android应用开发中,"Android-Application被回收引发空指针异常分析(消灭全局变量)"是一个常见的问题,尤其对于新手开发者来说,可能会遇到这样的困惑。这篇文章主要探讨了当Application对象被系统回收后,如何...

    android 捕捉异常并上传至服务器的简单实现

    为了更全面地捕获应用中的异常,可以在应用启动时注册一个Activity生命周期回调,以便在每个Activity的生命周期方法中插入异常处理代码。这样,即使在Activity中发生的异常也能被捕捉到。 ```java class ...

    Android-Fragment和Activity的传值通例子

    - 数据传递过程中需注意生命周期问题,确保在正确的时间点进行操作,避免因组件状态变化导致的数据丢失或异常。 - 当Fragment与Activity间的通信较为复杂时,推荐使用Interface回调,以保持代码的清晰性和可维护性。...

    模拟Activity进出栈.zip安卓程序源码资源下载

    这个"模拟Activity进出栈.zip"的安卓程序源码资源提供了深入理解Activity生命周期和任务栈管理的机会。以下是对相关知识点的详细解释: 1. **Activity生命周期**:每个Activity都有一个生命周期,包括创建...

    Activity isFinishing()判断Activity的状态实例

    在实际开发中,应尽量避免让Activity实例长时间被静态对象引用,以保持Activity生命周期的正常流转。同时,也可以使用工具如MAT(Memory Analyzer Tool)进行内存分析,以便及时发现和解决内存泄露问题。

    知晓当前实在哪一个活动中

    在Android应用开发中,"知晓当前实在哪一个活动中"是一个关键问题,这涉及到应用程序的生命周期管理和用户交互的理解。...对于调试、性能分析和异常处理等方面,复写Activity都是一个不可或缺的工具。

    安卓Android源码——模拟Activity进出栈.zip

    通过深入研究"安卓Android源码——模拟Activity进出栈.zip"中的内容,开发者可以更好地掌握Android系统如何处理Activity的生命周期和任务切换,从而编写出更符合用户习惯和系统性能的应用程序。对于进阶开发者来说,...

Global site tag (gtag.js) - Google Analytics