Android开发中Activity的启动模式

在学习Activity的启动模式时,也是分不清楚他们各自在返回栈中的运作机制,网上也有很多文章讲解了,现在我就将我学习过程中对Activity的启动模式的理解写出来,大家一起学习。
在实际的项目开发中,我们应该为不同的活动指定不同的启动模式。启动模式一共有四种,分别是standard、singleTop、singleTask、singleInstance。可以在AndroidManifest.xml中通过<activity>标签来指定android:launchMode 属性来选择启动模式。

在讲解Activity的启动模式之前,先来说说返回栈
Android是使用Task(任务)来管理活动的,一个任务就是一组存放在栈里的活动集,这样的栈就是返回栈(Back Stack)。栈是一种先进后出的数据结构(学习编程这点肯定要知道的,数据结构这门知识很必要)。在默认的情况下我们每启动一个新的活动,它就会在返回栈中入栈,并且处于栈顶的位置。同样每当我们按“返回键”或者调用finish( )方法时候,处于栈顶的活动就会出栈(先进先出),这个时候前一个入栈的活动就会处于栈顶位置(若此时栈内没有活动,程序就会退出)。程序显示给我们看到的总是处于栈顶位置的活动。

standard

standard是活动的默认启动模式。如果在AndroidManifest.xml中没有进行指定,那么所有活动都会自动使用这种启动模式。

现在我们创建一个Activity命名为FirstActivity,并且在活动中调用一个设有按钮的layout页面(first_activity_layout.xml),代码如下:
FirstActivity页面的onCreate( )方法代码:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("FirstActivity",this.toString());  
        setContentView(R.layout.first_activity_layout);
        Button button1=(Button)findViewById(R.id.button_1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
                startActivity(intent);
            }
        });
    }

first_activity_layout.xml页面代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <Button
        android:id="@+id/button_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="FirstActivity"/>
</LinearLayout>

因为standard是Activity的默认启动模式,因此就可以不再AndroidManifest.xml中进行指定
Intent intent=new Intent(FirstActivity.this,SecondActivity.class);看起来比较奇怪,在FirstActivity页面上启动FirstActivity,但是我们是研究standard的启动模式。

但是没关系,我们在代码中添加了日志打印代码Log.d("FirstActivity",this.toString());,用来打印当前活动的实例。

运行程序后当我们点击按钮几次后,会发现在日志中也打印了几次。从日志中我们看出,每次点击一次按钮就会创建一个新的FirstActivity实例,所以在返回栈中就存在多个FirstActivity实例,因此,当我们想要退出程序时就需要按返回键多次。

singleTop

singleTop模式:当活动的启动模式指定为singleTop,在启动活动时如果发现栈顶已经是该活动时,则直接使用该活动,不再创建新的实例。

1、当FirstActivity处于栈顶

首先在AndroidManifest.xml中将FirstActivity的启动模式修改下:

 <activity android:name=".FirstActivity"
              android:launchMode="singleTop"
              android:label="This is a FirstActivity">
             <!-- 定义当前活动的启动模式为singleTop,默认情况下是standard-->
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>

因为FirstActivity为该程序的主活动,所以在AndroidManifest.xml有主活动的声明。
在运行程序时,在下面的日志文件中会发现打印一次FirstActivity活动实例,当多次点击按钮时,就不在创建实例。因为FirstActivity的启动模式为singleTop,启动活动时发现栈顶已经是该活动,就会直接使用,并不创建。

2、当FirstActivity不在栈顶

首先修改FirstActivity中的onCreate ( )方法,使其点击按钮跳转到SecondActivity活动中。

FirstActivity活动的onCreate( )方法的代码:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("FirstActivity",this.toString());
        setContentView(R.layout.first_activity_layout);
        Button button1=(Button)findViewById(R.id.button_1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
                startActivity(intent);
            }
        });
    }

然后在SecondActivity的 second_activity_layout中设置按钮跳转到FirstActivity中。(实现两个活动中相互跳转,但是FirstActivity的启动方式是singleTop)
SecondActivity的onCreate( )方法的代码:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("SecondActivity",this.toString());
        setContentView(R.layout.second_activity_layout);
        Button button2=(Button)findViewById(R.id.button_2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(SecondActivity.this,FirstActivity.class);
                startActivity(intent);
            }
        });
    }

second_activity_layout.xml的布局代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <Button
        android:id="@+id/button_2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="SecondActivity"/>
</LinearLayout>

我们运行程序时,在FirstActivity界面点击按钮进入到SecondActivity界面中,在SecondActivity界面点击按钮,由会重新进入到FirstActivity界面。

com.example.chencong.activitytest_02.SecondActivity@53696bfc
com.example.chencong.activitytest_02.FirstActivity@536a18e8
com.example.chencong.activitytest_02.SecondActivity@5369a110
com.example.chencong.activitytest_02.FirstActivity@536ba028

我们可以看到系统创建了两个不同的FirstActivity实例,这是由于在SecondActivity界面中再次启动FirstActivity时,栈顶活动此时是SecondActivity,因此需要启动singleTop启动模式的FirstActivity时,系统就会创建一个新的FirstActivity实例。
现在按下“返回键”会返回在SecondActivity界面,再次按下“返回键”又会回到FirstActivity界面,再按一次“返回键”才会退出程序。

singleTop启动模式的原理图:
这里写图片描述

singleTask

当活动的指定模式为singleTask时,每次启动该活动时,系统首先在返回栈中检查是否存在该活动的实例(并不是检查栈顶),如果发现已经存在则直径使用实例,并将该活动之上的活动统统出栈(系统要保证显示给用户的活动在栈顶,所以要将该活动之上的活动统统出栈),如果没有发现就重新常见一个新的活动实例。

首先需要在AndroidManifest.xml中修改FirstActivity活动的启动模式:

 <activity android:name=".FirstActivity"
              android:launchMode="singleTask"
              android:label="This is a FirstActivity">
             <!-- 定义当前活动的启动模式为singleTop,默认情况下是standard-->
             <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中添加onRestart( )方法,并且打印日志:

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

重新运行程序打印日志信息:

 D/FirstActivity: com.example.chencong.activitytest_02.FirstActivity@536bb97c
 D/SecondActivity: com.example.chencong.activitytest_02.SecondActivity@536c1cd8
 D/FirstActivity: onRestart
 D/SecondActivity: onDestroy

从上面的日志信息中显示,在SecondActivity中启动FirstActivity时,会发现在返回栈中已经存在一个FirstActivity实例,并且是在SecondActivity下面(此时栈顶是SecondActivity),由于FirstActivity的启动方式为singleTask,于是SecondActivity就会出栈,并且FirstActivity就会成为栈顶活动。
因此FirstActivity活动就会onRestart( )方法就会被调用,而SecondActivity活动的onDestroy( )方法也会执行。并且现在返回栈中只剩下FirstActivity一个活动,当我们按下“返回键”时就会推出程序。

singleTask的启动原理图:

这里写图片描述

singleInstance

当活动的启动方式指定为singleInstance时,当启动活动时会启用一个新的返回栈来管理这个活动。
假象下,我们程序中有个返回栈属于自己活动可以进去的,但是当有别的活动(外部程序的活动)需要访问我们的活动时,就没办法与我们程序的活动在这个返回栈内,那就需要重新创建一个 返回栈来管理这种模式的活动。

现在我们在上面几种方式基础下面,创建活动FirstActivity、SecondActivity、ThirdActivity。活动SecondActivity的启动模式设置为singleInstance。

首先在AndroidManifest.xml中修改SecondActivity的启动模式:

 <activity android:name=".SecondActivity"
            android:launchMode="singleInstance">
 </activity>

然后修改FirstActivity活动中的 onCreate( )方法中的代码:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("FirstActivity","Task id is"+getTaskId());

        setContentView(R.layout.first_activity_layout);
        Button button1=(Button)findViewById(R.id.button_1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
                startActivity(intent);
            }
        });
    }

在返回栈中打印日志,打印当前返回栈中的id。

然后修改SecondActivity活动中的onCreate( )方法:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        Log.d("SecondActivity",this.toString());
        Log.d("SecondActivity", "Task id is " + getTaskId());
        setContentView(R.layout.second_activity_layout);
        Button button2=(Button)findViewById(R.id.button_2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(SecondActivity.this,FirstActivity.class);
                startActivity(intent);
            }
        });
    }

最后在ThirdActivity活动中修改onCreate( )方法:

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

仍然是在日志中打印当前返回栈中的id;

好了,现在运行程序,在日志中打印的 当前返回栈的 id信息为:

D/FirstActivity: Task id is 56
D/SecondActivity: Task id is 57
D/ThirdActivity: Task id is 56

从上面的日志信息中可以看出,FirstActivity和ThirdActivity是放在相同的返回栈中,但是SecondActivity是放在单独的返回栈中,并且栈中只有 一个活动。

同样,当我们按下“返回键”时,会发现ThirdActivity界面直接返回到了FirstActivity界面,再次按下“返回键”时,返回到SecondActivity界面,当我们再次按下“返回键”程序则会退出。

原因:
因为ThirdActivity和FirstActivity在一个返回栈中,首先ThirdActivity在栈顶,放我们按下“返回键”时,ThirdActivity出栈,位于其下面的FirstActivity就到了栈顶,显示为用户看到 ;当再次按下“返回键”,FirstActivity出栈后这个栈就为空了,那么系统就会显示另外一个返回栈的栈顶活动,因为此时SecondActivity位于另外的返回栈的栈顶,所以会在最后显示SecondActivity。当显示SecondActivity时再次按下“返回键”时,所有返回栈为空,程序退出。

singleInstance模式的原理图:

这里写图片描述

启动模式的原理图做的不是很好,文章如果有错误,希望广大网友指出,大家一起学习进步。

参考资料:
    1、第一行代码-Android
                --郭霖
    2、网上一些博客、文章和各大网友解答问题我学习得到的。
我挥舞着键盘和本子,
发誓要把世界写个明明白白。

By 2016年5月27日00:30:22

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章