Activity 知识梳理(1) - Activity生命周期

一、概述

学习Activity生命周期,首先我们要明白,学习它的目的不仅在于要知道有哪些生命周期,而是在于明白各回掉函数调用的时机,以便在合适的时机进行正确的操作,如初始化变量、页面展示、数据操作、资源回收等。平时的工作习惯都是,onCreate(xxx)初始化,onResume()注册、拉取数据,onPause()反注册,onDestroy()释放资源,这篇文章总结了一些和关键生命周期相关联的一些要点。

二、金字塔模型

Activity 生命周期金字塔模型.png

在官方文档中,把Activity的生命周期理解成为一个金字塔模型,它是基于下面两点:

  • 金字塔的每个阶梯表示Activity所处的状态
  • 回调函数则是各状态转换过程当中所经过的路径

这个模型中包含了Activity的六种状态:

  • Created:创建完成
  • Started:可见
  • Resumed:可见
  • Paused:部分可见
  • Stopped:不可见
  • Destroyed:销毁

在这六种状态当中,只有ResumedPausedStopped这几种状态在用户没有进一步操作时会保持在该状态,而其余的,都会在执行完相应的回调函数后快速跳过。

三、关键生命周期

3.1 protected void onCreate(Bundle savedInstanceState)

  • 该方法被回调时,意味着一个新的Activity被创建了。
  • 由于当前处于一个新的Activity实体对象当中,所有的东西都是未初始化的,我们一般需要做的事情包括调用setContentView方法设置该Activity的布局,初始化类成员变量。
  • onCreate(xxx)方法执行完之后,Activity就进入了Created状态,然而它并不会在这个状态停留,系统会接着回调onStart() 方法由Created状态进入到Started状态。
  • 注意到,onCreate(xxxx)是所有这些回调方法中唯一有参的,该参数保存了上次由于Activity被动回收时所保存的数据。

3.2 protected void onStart()

  • onStart()方法执行完后,Activity就进入了Started状态,它也同样不会在该状态停留,而是接着回调 onResume()方法进入Resumed状态。
  • onStart()被回调的情况有两种:
  • Created状态过来
  • Stopped状态过来,从这种状态过来还会先经过onRestart()方法。
  • 由于它也会从Stopped状态跳转过来,因此如果我们在onStop()当中反注册了一些广播,或者释放了一些资源,那么在这里需要重新注册或者初始化,可以认为,onStart()onStop()是成对的关系。
  • CreatedStarted都不是持久性的状态,那么为什么要提供一个onStart()回调给开发者呢,直接由Created状态或者是 Stopped状态经过onResume()这条路走到Resumed状态不就可以吗,那么我们就要分析一下从 onCreate()onStart(),再到onResume()的过程中,做了哪些其它的操作,这有利于我们在平时的开发中区分这两个回调的使用场景,我们来看一下源码。

首先我们看一下onStart()方法调用的地方,通过下面这段代码,我们可以知道ActivityonStart()最初是通过Activity#performStart()方法调用过来的:

<!-- Activity.java -->
private Instrumentaion mInstrumentation;

final void performStart() {
    mInstrumentation.callActivityOnStart(this);
}

<!-- Instrumentaion.java -->
public void callActivityOnStart(Activity activity) {
   activity.onStart();
}

Activity#performStart()方法是由ActivityThread#performLaunchActivity(调过来的:

<!-- ActivityThread.java -->
private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    Activity a = performLaunchActivity(r, customIntent); //performCreate, performStart()
    if (a != null) { 
        ....
        handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed); //performResume()
       ....
   }
}
//首先看一下调用performCreate, performStart的地方。
private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    mInstrumentation.callActivityOnCreate(activity, r.state); //performCreate
    ....
    if (!r.activity.mFinished) { 
        activity.performStart(); 
        r.stopped = false; 
    }
    if (!r.activity.mFinished) { 
       if (r.state != null) {         
           mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);  //1.onRestoreIntanceState()
       } 
    } 
    if (!r.activity.mFinished) { 
        activity.mCalled = false; 
        mInstrumentation.callActivityOnPostCreate(activity, r.state); //2.onPostCreate
        if (!activity.mCalled) { 
            throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onPostCreate()"); 
       } 
    }
    ...    
}
//这是performResume的入口。
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) {        
    ActivityClientRecord r = performResumeActivity(token, clearHide);
}
//最后看一下performResume真正执行的地方。
public final ActivityClientRecord performResumeActivity(IBinder token, boolean clearHide) {
    try { 
        if (r.pendingIntents != null) { 
            deliverNewIntents(r, r.pendingIntents); //3.onNewIntent()
            r.pendingIntents = null; 
        } 
       if (r.pendingResults != null) { 
            deliverResults(r, r.pendingResults); //4.onActivityResult()
            r.pendingResults = null; 
      } 
      r.activity.performResume(); 
}
  • 通过上面这段代码,我们可以得出以下几点启发:
  • onStart()onResume()的过程中,还可能会回调onRestoreInstanceState/onPostCreate/onNewIntent/onActvitiyResult这几个方法。
  • 如果应用退到后台,再次被启动(onNewIntent),或者通过startActivityForResult方法启动另一个 Activity得到结果返回(onActivityResult)的时候,在onStart()方法当中得到的并不是这两个回调的最新结果,因为上面的这两个方法并没有调用,而在onResume()当中,这点是可以得到保证的。

3.3 protected void onResume()

  • 该方法被回调时,意味着Activity已经完全可见了,但这个完全可见的概念并不等同于Activity所属的WindowAttach了,因为在Activity初次启动的时候,Attach的操作是在回调onResume()之后的,也就是下面的这段代码
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) {
    ....
    ActivityClientRecord r = performResumeActivity(token, clearHide);
    ....
    if (r.window == null && !a.mFinished && willBeVisible) {
        r.window = r.activity.getWindow();
        View decor = r.window.getDecorView();
        WindowManager.LayoutParams l = r.window.getAttributes();
        ...
        wm.addView(decor, l);
    }
}
  • onResume()方法被回调时,由于DecorView并不一定Attach了,因此这时候我们获取布局内某些View 的宽高得到的值有可能是不正确的,既然onResume()当中不能保证,那么onStart()方法也是同理,所有需要在attachToWindow之后才能执行或是期望得到正确结果的操作都需要注意这一点。
  • onResume当中,我们一般会做这么一些事:在可见时重新拉取一些需要及时刷新的数据、注册 ContentProvider的监听,最重要的是在onPause()中的一些释放操作要在这里面恢复回来。

3.4 protected void onPause()

  • 该方法被回调时,意味着Activity部分不可见,例如一个半透明的界面覆盖在了上面,这时候只要Activity仍然有一部分可见,那么它会一直保持在Paused状态。
  • 如果用户从Paused状态回到Resumed状态,只会回调onResume方法。
  • onPause()方法中,应该暂停正在进行的页面操作,例如正在播放的视频,或者释放相机这些多媒体资源。
  • onPause()当中,可以保存一些必要数据到持久化的存储,例如正在编写的信息草稿。
  • 不应该在这里执行耗时的操作,因为新界面启动的时候会先回调当前页面的onPause()方法,所以如果进行了耗时的操作,那么会影响到新界面的启动时间,官方文档的建议是这些操作应该放到 onStop()当中去做,其实在onStop()中也不应当做耗时的操作,因为它也是在主线程当中的,而在主线程中永远不应该进行耗时的操作。
  • 释放系统资源,例如先前注册的广播、使用的传感器(如GPS)、以及一些仅当页面获得焦点时才需要的资源。
  • 当处于Paused状态时,Activity的实例是被保存在内存中的,因此在其重新回到Resumed状态的过程中,不需要重新初始化任何的组件。

3.5 protected void onStop()

  • 该方法被回调时,表明Activity已经完全不可见了。
  • 在任何场景下,系统都会先回调onPause(),之后再回调onStop()
  • 官方文档有谈到,当onStop()执行完之后,系统有可能会销毁这个Activity实例,在某些极端情况下,有可能不会回调onDestroy()方法,因此,我们需要在onStop()当中释放掉一些资源来避免内存泄漏,而 onDestory()中需要释放的是和Activity相关的资源,如线程之类的(这点平时在工作中很少用,一般我们都是在onDestroy()中释放所有资源,而且也没有去实现onStart() 方法,究竟什么时候不会走onDestroy(),这点值得研究,目前的猜测是该Activity在别的地方被引用了,导致其不能回收)。
  • ActivityStopped状态回到前台时,会先回调onRestart()方法,然而我们更多的是使用onStart() 方法作为onStop()的对应关系,因为无论是从Stopped状态,还是从Created状态跳转到Resumed状态,都是需要初始化必要的资源的,而它们经过的共同路径是onStart()

3.6 protected void onDestroy()

  • 该方法被回调时,表明Activity是真正要被销毁了,
  • 此时应当释放掉所有用到的资源,避免内存泄漏。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,264评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,549评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,389评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,616评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,461评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,351评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,776评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,414评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,722评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,760评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,537评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,381评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,787评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,030评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,304评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,734评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,943评论 2 336

推荐阅读更多精彩内容