活动(Activity)的四种启动模式

在实际的项目中我们应该根据特定的需求为每个活动指定恰当的启动模式。
启动模式一共有4种。standard、singleTop、singleTasksingleInstance
通过在AndroidManifest.xml中给<activity>标签指定android:launchMode属性来选择启动模式。

1. 启动模式一:standard

活动的默认启动模式。在该模式下,每当启动一个新活动,它就会在返回栈中入栈,并处于栈顶的位置,并且,不管此活动是否已经存在于返回栈中,每次启动都会创建该活动的一个新实例。

Demo

这里我们在第一个项目(活动的跳转)的基础上进行讲解

在FirstActivity中的onCreate()方法中加入Log.d("FirstActivity",this.toString());,用于在创建活动实例时打印信息;在按钮点击函数start_secondavtivity()中更改代码实现在FirstActivity中打开FirstActivity

public class FirstActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("FirstActivity",this.toString());
        setContentView(R.layout.activity_first);
    }

    public void start_secondactivity(View view){
        Intent intent = new Intent(FirstActivity.this,FirstActivity.class);
        startActivity(intent);
    }
}

运行程序,然后点击两次按钮并查看打印信息:


图片.png

程序初运行会创建一个FirstActivity实例,并且每点击一次按钮就会创建一个新的FirstActivity实例,从打印信息中我们可以看出存在3个不同的FirstActivity,因此需要连按3次back键才能推出程序。


20190430_151404.gif
standard模式原理示意图:
standard模式原理示意图.png

2. 启动模式二:singleTop

在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。

Demo

修改AndroidManifest.xml中的FirstActivity的启动模式
android:launchMode="singleTop"

<activity android:name=".FirstActivity"
          android:launchMode="singleTop">
          <intent-filter>
             <action android:name="android.intent.action.MAIN" />
             <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
</activity>

然后重新运行程序


图片.png

我们发现不管点击多少次按钮,都只有一次打印信息,,因为目前FirstActivity已经处于返回栈的栈顶,每当想要再启动一个FirstActivity时都会直接使用栈顶的活动,因此FirstActivity只有一个实例,仅按一次back键就可退出程序。


20190430_153348.gif

不过当FirstActivity并未处于栈顶时,若再启动FirstActivity还是会创建新的实例。
下面来实验一下,修改FirstActivity中按钮响应的代码,将按钮跳转改为SecondActivity
public void start_secondactivity(View view){
        Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
        startActivity(intent);
    }

然后在SecondActivity中添加按钮函数start_firstactivity(),用来启动FirstActivity(注意先在布局中加入button控件),并在onCreate()中打印信息

activity_second.xml布局

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="start_firstactivity"
    android:text="button"
    android:layout_gravity="center"/>

SecondActivity

public class SecondActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("SecondActivity",this.toString());
        setContentView(R.layout.activity_second);
    }

    public void start_firstactivity(View view){
        Intent intent = new Intent(SecondActivity.this,FirstActivity.class);
        startActivity(intent);
    }
}

现在我们运行程序查看结果:
在FirstActivity中点击按钮启动SecondActivity,接着又在SecondActivity中点击按钮启动FirstActivity


20190430_160046.gif
打印信息.png

可以看到系统首先创建一个FirstActivity实例,我们点击按钮后系统又创建出一个SecondActivity实例,此时栈顶活动变为SecondActivity,再点击SecondActivity中的按钮,系统会创建一个新的FirstActivity实例。现在按下返回键会回到SecondActivity,再次按下返回键回到第一个FirstActivity,再按一次才退出程序。

singleTop模式原理示意图:
singleTop模式原理示意图.png

3. 启动模式三:singleTask

使用 single Top模式可以很好地解决重复创建栈顶活动的问题,但是如你在上一节所看到的,如果该活动并没有处于栈顶的位置,还是可能会创建多个活动实例的。那么有没有什么办法可以让某个活动在整个应用程序的上下文中只存在一个实例呢?这就要借助 singleTask模式来实现了。当活动的启动模式指定为 singleTask,每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。

Demo

修改AndroidManifest.xml中的FirstActivity的启动模式为singleTask

<activity android:name=".FirstActivity"
       android:launchMode="singleTask">
       <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
       </intent-filter>
</activity>

然后在FirstActivity中添加onRestart()方法,并打印日志:

    @Override
    protected void onRestart(){
        super.onRestart();
        Log.d("FirstActivity","onRestart");
    }

在SecondActivity中添加onDestroy()方法,并打印日志:

    @Override
    protected void onDestroy(){
        super.onDestroy();
        Log.d("SecondActivity","onDestroy");
    }

重新运行程序,在FirstActivity中点击按钮进入SecondActivity,然后在SecondActivity中点击按钮进入FirstActivity,查看日志:


图片.png

其实从打印信息中就可以明显看出了,在 SecondActivity中启动 FirstActivity时,会发现返回栈中已经存在一个FirstActivity的实例,并且在SecondActivity的下面,于是SecondActivity会从返回栈中出栈,而 FirstActivity重新成为了栈顶活动,因此 FirstActivityI的 onRestart()方法和 SecondActivity的 onDestroy()方法会得到执行。现在返回栈中应该只剩下一个 FirstActivityI的实例了,按一下Back键就可以退出程序。

singleTask模式原理示意图:
singleTask模式原理示意图.png

4. 启动模式四:singleInstance

singleInstance模式算是4种启动模式中最复杂的一个了,不同于以上三种模式,该模式下活动会启用一个新的返回栈来管理这个活动(其实如果singleTask模式指定了不同的taskAffinity,也会启动一个新的返回栈)。

那么这样做有什么意义呢?想象以下场景,假设我们的程序中有一个活动是允许其他程序调用的,如果我们想实现其他程序和我们的程序可以共享这个活动的实例,应该如何实现呢?使用前面3种启动模式肯定是做不到的,因为每个应用程序都会有自己的返回栈,同一个活动在不同的返回栈中入栈时必然是创建了新的实例。而使用singlelnstance模式就可以解决这个问题,在这种模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用的同一个返回栈,也就解决了共享活动实例的问题。

为了帮助你更好地理解这种启动模式,我们还是来实践一下。

Demo

首先创建一个新活动ThirdActivity及布局activity_third
Log.d("ThirdActivity","Task id is "+getTaskId());用于打印当前返回栈的id

public class ThirdActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("ThirdActivity","Task id is "+getTaskId());
        setContentView(R.layout.activity_third);
    }
}

修改FirstActivity中onCreate()打印日志代码为Log.d("FirstActivity","Task id is "+getTaskId());

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("FirstActivity","Task id is "+getTaskId());
        setContentView(R.layout.activity_first);
    }

SecondActivity中同样修改这句代码,并使按钮响应事件改为跳转至ThirdActivity

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("SecondActivity","Task id is "+getTaskId());
        setContentView(R.layout.activity_second);
    }
 public void start_firstactivity(View view){
        //Intent intent = new Intent(SecondActivity.this,FirstActivity.class);
        Intent intent = new Intent(SecondActivity.this,ThirdActivity.class);
        startActivity(intent);
    }

最后修改 AndroidManifest.xml中SecondActivity的启动模式为singleInstance

<activity android:name=".SecondActivity"
      android:launchMode="singleInstance">
      <intent-filter>
           <action android:name="com.example.activity_intent.ACTION_START"/>
           <category android:name="android.intent.category.DEFAULT"/>
           <category android:name="com.example.activity_intent.MY_CATEGORY"/>
       </intent-filter>
</activity>

现在运行程序,在FirstActivity中进入SecondActivity,从SecondActivity中进入ThirdActivity ,查看日志


20190509_105100.gif
图片.png

图片.png

图片.png

可以看到, SecondActivity的Task id不同于FirstActivity和ThirdActivity,这说明SecondActivity确实是存放在一个单独的返回栈里的,而且这个栈中只有SecondActivity这一个活动。

然后我们按下Back键进行返回,你会发现ThirdActivity竟然直接返回到了FirstActivity,再按下Back键又会返回到SecondActivity,再按下Back键才会退出程序,这是为什么呢?

其原理很简单,由于FirstActivity和ThirdActivity是存放在同一个返回栈里的,当在ThirdActivity的界面按下Back键,ThirdActivity会从返回栈中出栈,那么FirstActivity就成为了栈顶活动显示在界面上,因此也就出现了从ThirdActivity直接返回到FirstActivity的情况。然后FirstActivity界面再次按下Back键,这时当前的返回栈已经空了,于是就显示了另一个返回栈的栈顶活动,即SecondActivity。最后再次按下Back键,这时所有返回栈都已经空了,也就自然退出了程序。

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

推荐阅读更多精彩内容