Android 四种启动模式

2017/9/27 posted in  Android

此博文用来介绍一下Activity的四种启动模式和应用的场景。

主要是对于Android Activity启动模式的梳理。

返回栈

首先,在介绍之前,我们需要有一个返回栈的概念。栈,是一种数据结构,遵循的是先进后出的规则,而无论是什么APP,里面的N个Activity最终都会被压入栈(或弹出栈中),而与我们进行交互的就是处于栈顶的Activity,在这里就需要介绍一个返回栈的概念。上一张图。

20171104150978353486802.png
20171104150978353486802.png

相信这张图片很清楚的说明,入栈的方式,所谓先进后出就是现在我们假设栈里面已经有abcd四个Activity了,并且都市默认的standard方式启动的。所谓先进后出就是,当我们不断按下back(返回键)的时候,后进去的d反而是最先谈栈的,a最后弹栈。

压栈:a->b->c->d(入栈顺序)

弹栈:b->c->b->a(弹出顺序)

先有一个返回栈的概念才能更好的进行分析。

为什么需要启动方式

比如说,现在栈里面已经有四个activity abcd,这个时候的需求是需要到一个Activity a,如果我们只是使用默认的启动方式,那么就会形成abcda,当用户点击back按键的时候,弹出a,abcd。再按弹出d,abc。以此类推,然后又看到了a,是不是很烦人,而且这样的设计也的确很不人性化,所以设计了四种启动方式,来优化更好的体验,和满足特定场景下的用户需求。

四种启动的方式

(1) standard: 标准的方式,也是系统默认的方式,每次启动一个activity,都会去创建一个新的activity的实例,并让该实例出于activity的栈顶位置,与用户交互,不管该实例存在不存在。

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent= new Intent(FirstActivity.this,FirstActivity.class);
        startActivity(intent);
    }
});

这是一个很典型的例子,就是由FirstActivity,跳转到FirstActivity,我们直观的思维会认为FirstActivity已经存在了,那么直接复用就好了啊,但是事实却不是这样子的。点击button两下,算上本身创建的FirstActivity,现在栈里面相当于有三个FirstActivity的实例,必须要点击三次返回键,才能回到桌面。这就是standard模式。

(2) singleTop :栈顶复用模式,当一个新的Activity已经处于栈顶的时候,那么再次调用新的Activity将不会创建新的实例,因为新的Activity已经在栈顶了,可以直接复用。比如说是栈的情况是abcd,d的启动方式singleTop,那么当我们再次启动d的时候,并不会创建新的实例,还是abcd,因为栈顶已经是d了,如果是adbc那么创建以后就是adbcd。如果d是默认方式启动的话,得到的就是abcdd。

(3) singleTask :栈内复用的模式,只要activity在一个栈中,那么多次启动此activity都不会重新创建实例,比如说只要Activity以singleTask的方式启动,系统首先会去找寻是否有A需要的栈s,来存放A实例。如果没有s,就会创建栈s。然后做判断,如果s中有A,那么就直接把A置于栈顶,并且清空A上面的所有实例。如果A已经在栈顶,那么就直接复用A。如果存在S,就看S中是否有A的实例,没有A的实例就直接创建并且入栈,如果已经有A的实例并且没有处于栈顶,那就清楚A之前的所有实例,让A处于栈顶。举个例子(D都是以singleTask启动):

  • 比如目前S1中的情况为ABC,D以singleTask的启动方式入栈,并且请求的任务栈为S2,那么很显然,并不存在S2,所以就会创建S2,并且把D压入S2中。S1:ABC,S2:D。
  • 另一种情况,S1:ABC ,D请求的是S1,那么直接压入S1中。S1:ABCD.
  • S1:ADBC ,D请求的是S1,那么最终的S1:AD。因为栈内复用方式默认有一个clearTop的方法,会导致D上所有的实例出栈。让D置为栈顶。

(4) singleInstance: 单实例模式。这是一个加强版本的singleTask模式,它除了具有singleTask模式的所有特性之外,还加强的了一点,就是次方式启动的Activity只能单独的位于一个任务栈中,由于栈复用的特性,后续均不会创建新的Activity,除非这个任务栈被系统的销毁了。

特殊情况

比如说现在有前台任务栈s1:AB(A底,B顶),后台任务栈S2:CD(都是以singleTask的方式启动)。当s1请求D入栈的时候,最终得到的是ABCD,但是如果只是请求C入栈的时候,得到的是ABC.

设置启动模式(两种)

第一种:Xml文件中配置

<activity
    android:name=".FirstActivity"
    android:label="hello My First Activity"
    android:launchMode="singleTop"/>

第二种:设置intent标志位的时候配置

Intent intent= new Intent();
intent.setClass(FirstActivity.this,FirstActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

第二种的优先级方式是高于第一种的,当两种都存在的时候,以第二种为准。

指定栈和获取当前的activity的栈ID

指定栈就是在设置activity的时候指定taskAffinity的属性,要求一个字符串,用.来分包,如果不写,栈就是默认的包名。在这里我指定为了,yanshui.site.task01配合上singleTask使用

<activity
    android:name=".FirstActivity"
    android:label="hello My First Activity"
    android:launchMode="singleTask"
    android:taskAffinity="yanshui.site.task01"/>
//当你想要知道你当前的activity处于栈的id的时候直接在该activity中调用此方法即可。
Log.d(TAG, "task id is "+getTaskId());

使用场景

  • Standard 默认启动方式,每次都会创建一个新的实例,如果开发中需要就使用。但应该注意具体的情况,优化设计体验。
  • singleTop 栈顶复用,适合接受推送消息的展示页。某些本应只展示一个的场景,比如一下子收到一堆推送消息,不能每个都弹出来吧。但凡是这样的都行。优酷的推荐视频,电商app推送一个活动。每次只是显示第一条消息。
  • singleTask 栈复用模式适合程序入口,不需要启动后activity的是的,只能用在启动页了吧,首页,这种只允许有一个instance,如果有其他就移除掉它顶上的。从这个Activity进入的其他activity都抛弃掉了。用户要再操作一次。
  • singleInstance 栈单实例模式,需要一个activity提供给多有的应用程序访问的,比如说闹钟,响过一次以后,不再提醒,然后就不会再响了。

标志位

  • FLAG_ACTIVITY_NEW_TASK(xml中指定为singleTask)

  • FLAG_ACTIVITY_SINGLE_TOP(xml中指定为singleTop)

简单小结一下

使用方式:

standard:怎么样都要创建

singleTop:顶上不是target Activity,new一个

singleTask:顶上不是target Activity,移除target之上的,把自己变成top。

singleInstance:开辟私有的task,完全独立于程序的其他activity的task。

使用场景:

standard:普通activity

singleTop:要展示推送过来的消息

singleTask:程序入口等启动页面

singleInstance:完全独立的,类似闹钟的提示