Activity四种启动模式的理解

众所周知,activity的启动模式有standard/singleTop、singleTask/singleInstance四种,之所以将四者两两分组,是因为她们都有相似的地方,下面就具体说说对这四种启动模式的理解

standard

即标准模式,也就是Android activity的的默认启动模式,每次启动一个activity都会创建一个新的实例,不管这个实例是否已经存在,新创建的activity实例的生命周期就是典型的activity的生命周期。

singleTop

栈顶复用模式,之所以将它跟standard放在一起,是因为当开启一个启动模式为singleTop的activity时,如果当前开启的activity在所需任务栈已存在实例,并且该实例位于栈顶(唯一条件),那么就不会创建新的activity示例,而是直接复用该栈顶的实例,并且会回调activity的onNewIntent方法。如果不是位于栈顶或者任务栈中根本不存在该实例则跟standard模式是一样的。举个例子来说,假设当前任务栈内的情况是ABCD,如果D的启动模式为singleTop,这时候再去启动D,则回复用D的示例,任务栈的情况还是ABCD,不会创建新的D的实例。如果启动A(或B或C),即使A(或B或C)也是singleTop模式,也不能进行实例复用,栈内情况为:ABCDA(或B或C)。再或者启动D,但D的启动模式是standard,则任务栈内的情况为ABCDD。

singleTask

栈内复用。根据singleTop,大概就知道singleTask是一个什么情况了。这种模式下,只要在Activity任务栈中存在,多次启动此Activity(当然要是同一个任务栈)都不会重新创建实例,和singleTop模式一样,系统也会回掉onNewIntent方法。具体来说,当一个具有singleTask模式的Activity A请求启动后,系统首先会寻找是否存在A想要的任务栈,如果不存在,就重新创建一个任务栈,再讲A进栈。如果存在A所需的任务栈,这时就要看A的实例是否已经存在,如果存在,则复用A实例,并将A实例调到栈顶以及调用它的onNewIntent方法,并将之前在A上的其他Activity实例清空出栈。如果实例不存在,就创建A的实例并把A压入栈中。还是来举个例子:
1.假如已经存在任务栈S1,这时D以singleTask模式启动,但是D所需的任务栈并不是S1,而是S2,这是后会首先创建任务栈S2,然后将D的实例放进去。
2.第二种情况,还是接着第一种情况,D所需的任务栈就是S1,但是S1中并不存在D的实例,这是后D直接就被放进任务栈S1了。
3.第三种情况是,D所需的任务栈就是S1,并且当前S1栈内情况是ADBC,这时候以singleTask模式启动D,S1栈内情况将会变为AD,D被调到了栈顶,BC被清空出栈。同时回调onNewIntent方法。
我们都知道指定Activity的启动模式,有两种途径,分别是:
指定Activity的启动模式有两种方式,

<activity
          android:name=".SecondActivity"
          android:launchMode="singleTask"/>
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
singleInstance

它是单实例模式。它除了具有singleTask模式的所有特性外,通俗的说就是拥有singleInstance模式的Activity A在一个任务栈只会存在一个它的实例,并且不会存在其他Activity的实例。如果有后续的Activity的进来,如果是A则会栈内复用,不会创建新的实例。这里来举一个例子。假设有MainActivity、SecondActivity、ThirdActivity。其中SecondActivity是singleInstance模式,其他两个是standard模式

//启动SecondActivity
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
//启动ThirdActivity
Intent intent = new Intent(SecondActivity.this,ThirdActivity.class);
startActivity(intent);

这时在查看任务栈情况:


image.png

解释一下,由于SecondActivity是singleInstance模式,则启动它时,创建一个新的任务栈并将SecondActivity放了进去,当用SecondActivity启动其他Activity时,为了保证傲娇的SecondActivity独享任务栈,这是又要重新创建一个与SecondActivity不同的任务栈,将ThirdActivity放进去。所以这三个Activity都在不同的任务栈。

至于应用场景:
standard模式就是一般的正常的activity跳转。
singleTask适合作为程序入口activity。
singleTop适合接收通知启动的内容页面。比如:点击通知都是到达一个特定的页面,如果不使用复用就会有打开多个相同的页面。
singleInstance模式适合闹铃提醒,因为闹铃提醒跟其他的功能是分离的。

onNewIntent

当Activity的启动模式设置为singleTop(),singleTask(),singleInstance()时,重新启动该Activity时不会调用其onCreate()方法,而是调用onNewIntent(Intent intent)方法通过设置setIntent(intent)来刷新数据。

onSaveInstanceState

先看下官方文档的介绍:

    /**
     * Called to retrieve per-instance state from an activity before being killed
     * so that the state can be restored in {@link #onCreate} or
     * {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method
     * will be passed to both).
     *
     * <p>This method is called before an activity may be killed so that when it
     * comes back some time in the future it can restore its state.  For example,
     * if activity B is launched in front of activity A, and at some point activity
     * A is killed to reclaim resources, activity A will have a chance to save the
     * current state of its user interface via this method so that when the user
     * returns to activity A, the state of the user interface can be restored
     * via {@link #onCreate} or {@link #onRestoreInstanceState}.
     *
     * <p>Do not confuse this method with activity lifecycle callbacks such as
     * {@link #onPause}, which is always called when an activity is being placed
     * in the background or on its way to destruction, or {@link #onStop} which
     * is called before destruction.  One example of when {@link #onPause} and
     * {@link #onStop} is called and not this method is when a user navigates back
     * from activity B to activity A: there is no need to call {@link #onSaveInstanceState}
     * on B because that particular instance will never be restored, so the
     * system avoids calling it.  An example when {@link #onPause} is called and
     * not {@link #onSaveInstanceState} is when activity B is launched in front of activity A:
     * the system may avoid calling {@link #onSaveInstanceState} on activity A if it isn't
     * killed during the lifetime of B since the state of the user interface of
     * A will stay intact.
     *
     * <p>The default implementation takes care of most of the UI per-instance
     * state for you by calling {@link android.view.View#onSaveInstanceState()} on each
     * view in the hierarchy that has an id, and by saving the id of the currently
     * focused view (all of which is restored by the default implementation of
     * {@link #onRestoreInstanceState}).  If you override this method to save additional
     * information not captured by each individual view, you will likely want to
     * call through to the default implementation, otherwise be prepared to save
     * all of the state of each view yourself.
     *
     * <p>If called, this method will occur before {@link #onStop}.  There are
     * no guarantees about whether it will occur before or after {@link #onPause}.

onSaveInstanceState主要是在Activity异常情况下保存Activity状态调用的方法,异常情况我们主要分析下面两种:

资源相关的系统配置发生改变导致Activity被杀死并重新创建

拿最简单的图片来说,当我们使用图片时会将图片放在drawable目录下,需要使用时就通过Resourse获取图片,同时为了适配各种屏幕的手机,也需要在drawable-hdpi、drawable-mdpi、drawable-xhdpi、drawable-xxhdpi放在对应分辨率的图片。当应用启动时,会根据当前设备去获取对应的图片。比如横屏手机和竖屏手机会拿到不同的图片,如果当前应用支持landscape和portrait时。如果当前Activity突然发生横竖屏切换,这个时候Activity就会被销毁进行重启。当然这种情况下我们也可以禁止Activity重启,通过配置configChanges参数。也可以在各个view中通过

android:saveEnabled

属性设置为false,或者通过滴啊勇setSaveEnabled方法显示阻止布局内的视图保存其状态。您通常不应该姜该属性停用,但是如果您想以不同方式恢复Activity UI的状态,就可能需要这样做。
onSaveInstanceState如果调用事发生在onStop方法之前,但是不能保证与onPause的执行顺序。

资源内存不足导致优先级低的Activity被杀死

Android按照优先级从高到低,可以分为三种:

  • 1.前台Activity:正在与用户交互的Activity,优先级最高
  • 2.可见但非前台Activity:比如Activity弹出了一个对话框,导致Activity可见但是位于后台无法和用户直接交互
  • 3.后台Activity:已经被暂停的Activity,执行了onStop,优先级最低
onRestoreInstanceState
    /**
     * This method is called after {@link #onStart} when the activity is
     * being re-initialized from a previously saved state, given here in
     * <var>savedInstanceState</var>.  Most implementations will simply use {@link #onCreate}
     * to restore their state, but it is sometimes convenient to do it here
     * after all of the initialization has been done or to allow subclasses to
     * decide whether to use your default implementation.  The default
     * implementation of this method performs a restore of any view state that
     * had previously been frozen by {@link #onSaveInstanceState}.
     *
     * <p>This method is called between {@link #onStart} and
     * {@link #onPostCreate}.
     *
     * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}.
     *
     * @see #onCreate
     * @see #onPostCreate
     * @see #onResume
     * @see #onSaveInstanceState
     */
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        if (mWindow != null) {
            Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
            if (windowState != null) {
                mWindow.restoreHierarchyState(windowState);
            }
        }
    }

onRestoreInstanceState是在onStart方法之后调用。关于保存和恢复View的层次结构,系统的工作流程时这样的:首先Activity被意外终止时,Activity会调用onSaveIntsanceState去保存数据,然后Activity会委托Window去保存数据,接着Window再委托它上面的顶级容器去保存数据。顶层容器是一个ViewGroup,一般来说它可能时DecorView。最后顶层容器再去意义通知它的子元素来保存数据,这样整个数据保存过程就完成了。可以发现,这是一种典型的委托思想,上层委托下层,父容器委托子元素去处理一件事情。当然还有很重要的一点就是,当父容器去一一通知子元素的时候,子元素必须得有对应的id,不然就没法找到,对应view数据就不能恢复

onConfigurationChanged

onConfigurationChanged对应着AndroidManifest中activity标签下的android:configChanges="",这里的取值为:


image.png

参考源码说明,
image.png

默认情况下,如果上表的配置发生改变,Activity将会被重启。有时为了当某些配置发生改变时,不让Activity重启,这时我们可以通过android:configChanges=""来指定一些配置,当它发生改变时,Activity不会重启,而只是回调了Activity的onConfigurationChanged方法。如果某些配置发生改变,而未在Activity的android:configChanges=""中,那么Activity还是会重启,而onConfigurationChanged就不会被调用。至于应用场景,加入我们应用的某些activity在屏幕方向发生改变时,这时不想让Activity进行重启,这时就可以在对应的Activity标签中申明android:configChanges="orientation|screenSize" ,然后当屏幕方向发生改变时,该Activity不会发生重启,而是回调onConfigurationChanged方法,然后我们就可以在这个方法里处理屏幕发生改变之后的情况。

FLAG_ACTIVITY_NEW_TASK

使用一个新的Task来启动一个Activity,新的Task表示Activity所需的Task,所以这是后有两种情况,一种是与启动的Activity同一Task,一种该Activity指定了taskAffinity字段,并且与启动它的Actvity TaskName不一样,这时才会重新新建一个Task。

FLAG_ACTIVITY_SINGLE_TOP

使用singleTop模式启动该Activity,与在AndroidManifest中指定android:launchMode=“singleTop”的效果是一样的

FLAG_ACTIVITY_CLEAR_TOP

使用singleTask模式启动该Activity,与在AndroidManifest中指定android:launchMode=“singleTask”的效果是一样的

FLAG_ACTIVITY_NO_HISTORY

Activity使用这种模式启动Activity,当该Activity启动其他Activity后,该Activity就消失了,不会保留在Activity栈中。

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

推荐阅读更多精彩内容