转载

安卓开发笔记——深入Activity

在上一篇文章《 安卓开发笔记——重识Activity 》中,我们了解了Activity生命周期的执行顺序和一些基本的数据保存操作,但如果只知道这些是对于我们的开发需求来说是远远不够的,今天我们继续探索Activity,来了解下关于Activity任务栈和Activity四种启动模式的区别。

在讲Activity任务栈前,我们应该先知道什么是栈?

简单点来理解,可以把栈比作一个开封的箱子,我们可以往里面塞东西,这里假设塞的东西的底面积和箱子的底面积是相同的,那么这些东西就具备有从下往上一定的顺序,当我们想要取出箱子里面的东西时,我们没有办法一下子拿到箱子最底层的东西,我们只能拿到最上面一层的东西,从上往下。

来看下这张图,这里的箱子就是栈,箱子口可以看作是栈的入口与出口,东西代表数据。栈的特点:具有一定的次序,后进先出(越先放入的东西,越晚出来)。

安卓开发笔记——深入Activity

1、Activity任务栈

好了,在了解了什么是栈之后,我们可以开始进入今天的主题了,在Android的官方文档描述中我们可以知道,任务栈也是栈,具有栈的一切特点。

Activity任务栈,顾名思义是存放Activity任务的栈,这里的任务栈为上图箱子,Activity为上图的东西。

当我们每打开一个Activity的时候它会就往Activity任务栈中压入一个Activity,当我们每销毁一个Activity的时候它会从Activity任务栈中弹出一个Activity,由于安卓系统自身的设计,我们只能在手机屏幕上获取当前一个Activity的焦点即栈顶元素(最上面的Activity),其余的Activity会暂居后台等待系统调用。

安卓开发笔记——深入Activity

1.1、关于任务栈的概念:

任务栈是用来提升体验 而设计的:
(1) 程序打开时就创建了一个任务栈, 用于存储当前程序的activity,当前程序(包括被当前程序所调用的)所有的activity属于一个任务栈。
(2) 一个任务栈包含了一个activity的集合, 可以有序的选择哪一个activity和用户进行交互,只有在任务栈栈顶的activity才可以跟用户进行交互。
(3) 任务栈可以移动到后台,并且保留了每一个activity的状态. 并且有序的给用户列出它们的任务, 而且还不丢失它们状态信息。
(4) 退出应用程序时,当把所有的任务栈中所有的activity清除出栈时,任务栈会被销毁,程序退出。

1.2、关于任务栈的缺点:

(1) 每开启一次页面都会在任务栈中添加一个Activity,而只有任务栈中的Activity全部清除出栈时,任务栈被销毁,程序才会退出,这样就造成了用户体验差,需要点击多次返回才可以把程序退出了。
(2) 每开启一次页面都会在任务栈中添加一个Activity还会造成数据冗余 重复数据太多,会导致内存溢出的问题(OOM)。

2、Activity的4种启动方式

为了解决任务栈产生的问题,Android为Activity设计了启动模式,那么下面的内容将介绍Android中Activity的启动模式,这也是最重要的内容之一。

启动模式(launchMode)在多个Activity跳转的过程中扮演着重要的角色,它可以解决是否生成新的Activity实例,是否重用已经存在的Activity实例,是否和其他实例共用一个任务栈。任务栈是一个具有栈结构的对象,一个任务栈可以管理多个Activity,每启动一个应用,也就创建一个与之对应的任务栈。

Activity一共有以下四种launchMode模式:1、standard 2、singTop 3、singTask 4、singleInstance,我们可以在AndroidManifest.xml配置 <activity> 的android:launchMode属性为以上四种之一即可。
2.1、实践是检验真理的唯一标准
下面写个实例,有2个Activity,每个Activity都有一个跳转按钮,第一个Activity点击按钮跳转第二个Activity,第二个Activity点击按钮跳转自身。
安卓开发笔记——深入Activity
 1 package com.lcw.rabbit.activitydemo;  2   3 import android.app.Activity;  4 import android.content.Intent;  5 import android.os.Bundle;  6 import android.util.Log;  7 import android.view.View;  8 import android.widget.Button;  9  10 public class MainActivity extends Activity { 11  12     private static final String TAG = "Rabbit"; 13  14     private Button mbButton; 15  16     @Override 17     protected void onCreate(Bundle savedInstanceState) { 18         super.onCreate(savedInstanceState); 19         setContentView(R.layout.activity_main); 20  21         Log.i(TAG,"第一个Activity,加入任务栈:"+getTaskId()); 22  23         //点击按钮跳转第二个Activity 24         mbButton = (Button) findViewById(R.id.bt_button); 25         mbButton.setOnClickListener(new View.OnClickListener() { 26             @Override 27             public void onClick(View v) { 28                 startActivity(new Intent(MainActivity.this, SecondActivity.class)); 29             } 30         }); 31  32  33     } 34  35     @Override 36     protected void onDestroy() { 37         super.onDestroy(); 38         Log.i(TAG, "第一个Activity,退出任务栈:" + getTaskId()); 39     } 40 }
MainActivity.java
安卓开发笔记——深入Activity
 1     package com.lcw.rabbit.activitydemo;  2   3     import android.app.Activity;  4     import android.content.Intent;  5     import android.os.Bundle;  6     import android.util.Log;  7     import android.view.View;  8     import android.widget.Button;  9  10     public class SecondActivity extends Activity { 11  12         private static final String TAG = "Rabbit"; 13         private Button mbButton1; 14         private Button mbButton2; 15  16         @Override 17         protected void onCreate(Bundle savedInstanceState) { 18             super.onCreate(savedInstanceState); 19             setContentView(R.layout.activity_second); 20             Log.i(TAG, "第二个Activity,加入任务栈:" + getTaskId()); 21             //点击按钮跳转第二个Activity 22             mbButton1 = (Button) findViewById(R.id.bt_button1); 23             mbButton1.setOnClickListener(new View.OnClickListener() { 24                 @Override 25                 public void onClick(View v) { 26                     startActivity(new Intent(SecondActivity.this, MainActivity.class)); 27                 } 28             }); 29             mbButton2 = (Button) findViewById(R.id.bt_button2); 30             mbButton2.setOnClickListener(new View.OnClickListener() { 31                 @Override 32                 public void onClick(View v) { 33                     startActivity(new Intent(SecondActivity.this, SecondActivity.class)); 34                 } 35             }); 36         } 37  38         @Override 39         protected void onDestroy() { 40             super.onDestroy(); 41             Log.i(TAG, "第二个Activity,退出任务栈:" + getTaskId()); 42         } 43     }
SecondActivity.java

安卓开发笔记——深入Activity 安卓开发笔记——深入Activity

实验一:启动模式standard

系统默认的Activity启动模式是standard,我们这里为了检验再设置一下:

 1         <activity  2             android:name=".MainActivity"  3             android:launchMode="standard">  4             <intent-filter>  5                 <action android:name="android.intent.action.MAIN" />  6                 <category android:name="android.intent.category.LAUNCHER" />  7             </intent-filter>  8         </activity>  9         <activity 10             android:name=".SecondActivity" 11             android:launchMode="standard"></activity>

现在我们进入第一个Activity的时候,点击按钮启动第二个Activity,看下当前任务栈:

安卓开发笔记——深入Activity

当我们点击第二个Activity的按钮,让它跳转自身,看下当前任务栈:

安卓开发笔记——深入Activity

当我们按下Back键,跳转第一个Activity,销毁第二个Activity时,看下当前任务栈:

安卓开发笔记——深入Activity

可以发现,这个任务栈和我们刚上图描述的箱子(Activity任务栈)是一致的,从上往下放东西(Activity),越晚放进去的东西(Activity)在越上面,而后面的t1072则代表当前任务栈的编号ID,ID相同代表它们属于同一个任务栈。

我们从日志文件中也可以看得很清楚:

安卓开发笔记——深入Activity

实验一结论:

在Activity启动模式为standard(默认)的情况下,不管之前有没有Activity实例,每一次启动Activity都会创建一个新的Activity实例,并置于Activity任务栈栈顶。

实验二:启动模式singleTop

 1        <activity  2             android:name=".MainActivity"  3             android:launchMode="standard">  4             <intent-filter>  5                 <action android:name="android.intent.action.MAIN" />  6                 <category android:name="android.intent.category.LAUNCHER" />  7             </intent-filter>  8         </activity>  9         <activity 10             android:name=".SecondActivity" 11             android:launchMode="singleTop"></activity>

现在我们进入第一个Activity点击按钮跳转第二个Activity,然后再点击按钮跳转自身,看下当前任务栈:

安卓开发笔记——深入Activity

系统日志文件:

安卓开发笔记——深入Activity

然后我们再点击按钮启动第一个Activity,然后点击按钮启动第二个Activity,看下当前任务栈:

安卓开发笔记——深入Activity

系统日志:

安卓开发笔记——深入Activity

实验二结论:

在Activity启动模式为singleTop(栈顶任务唯一)的情况下,如果当前Activity处于栈顶,那么它就不会再去实例化一个新的Activity,当Activity不处于栈顶的时候,会重新实例化一个新的Activity并置于栈顶,此时的任务栈编号为1080。

实验三:启动模式singleTask

 1         <activity  2             android:name=".MainActivity"  3             android:launchMode="standard">  4             <intent-filter>  5                 <action android:name="android.intent.action.MAIN" />  6                 <category android:name="android.intent.category.LAUNCHER" />  7             </intent-filter>  8         </activity>  9         <activity 10             android:name=".SecondActivity" 11             android:launchMode="singleTask"></activity>

当我们进入第一个Activity点击进入第二个,再启动自身,看下当前任务栈:

安卓开发笔记——深入Activity

系统日志:

安卓开发笔记——深入Activity

现在我们点击启动第一个Activity,再点击启动第二个Activity,看下当前任务栈:

安卓开发笔记——深入Activity

系统日志:

安卓开发笔记——深入Activity

实验三结论:

在Activity启动模式为singleTask(唯一实例)的情况下,当启动Activity的时候,如果当前Activity不存在则实例化一个新的Activity,如果当前Activity在任务栈中已经存在,则会复用这个Activity实例,但这边我们从日志打印可以看出在启动第二个Activity的时候,第一个Activity推出了任务栈,也就意味着当启动模式为singTask的时候,启动已经存在在Activity任务栈中但不在栈顶的Activity时,该Activity会把压在它前面的所有Activity弹出任务栈,此时任务栈编号为1081,属于同一个任务栈。

实验四:启动模式singleInstance

 1         <activity  2             android:name=".MainActivity"  3             android:launchMode="standard">  4             <intent-filter>  5                 <action android:name="android.intent.action.MAIN" />  6                 <category android:name="android.intent.category.LAUNCHER" />  7             </intent-filter>  8         </activity>  9         <activity 10             android:name=".SecondActivity" 11             android:launchMode="singleInstance"></activity>

现在我们进入第一个Activity点击按钮跳转第二个Activity,再让其跳转自身,看下当前任务栈:

安卓开发笔记——深入Activity

系统日志:

安卓开发笔记——深入Activity

现在点击按钮启动第一个Activity,再点击按钮启动第二个Activity,看下当前任务栈:

安卓开发笔记——深入Activity

系统任务:

安卓开发笔记——深入Activity

点击Back键,直到程序完全退出,看下系统日志:

安卓开发笔记——深入Activity

实验四结论:

在Activity启动模式为singleInstance的情况下,首先我们可以发现的是启动模式为singleInstance的Activity处于不同的任务栈(Task编号不同),并保证不再有其他的Activity实例进入,它还是和singleTask一样保持唯一实例,然后它的退出顺序是不再是根据调用顺序,而是在不同的任务栈中,从上往下退出。

在共用一个Activity实例时,期间发生了什么?

在上诉模式中,当我们的Activity涉及到同一实例的时候,期间Activity做了哪些事情?在Android官方文档中我们可以知道期间虽然没有新实例化一个Activity,但是调用了onNewIntent方法。

现在我们在第二个Activity里添加一个onNewIntent方法:

1         @Override 2         protected void onNewIntent(Intent intent) { 3             super.onNewIntent(intent); 4             Log.i(TAG, "第二个Activity,执行onNewIntent"); 5         }

1、在standard(默认)启动模式下,我们来回的去跳转Activity,看下日志打印,发现是不会调用onNewIntent方法的,因为它不是一个实例。

安卓开发笔记——深入Activity

2、在singleTop模式下,我们从第一个Activity跳转到第二个Activity,再从第二个Activity跳转自身,再跳转第一个Activity,看下日志打印,我们可以发现,当第二个Activity置于栈顶的时候,由于重用了实例,所以调用了onNewIntent方法。

安卓开发笔记——深入Activity

3、当singleTask和singleInstance模式下也是一样的,因为重用了实例,所以会调用onNewIntent方法,且onNewIntent方法是在前一个Activity的onStop方法后(当前ActivityonReStart方法前)立即调用的。

好了,今天先写到这里,有什么建议或疑问,可以在文章评论给我留言。

作者:李晨玮

出处: http://www.cnblogs.com/lichenwei/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我!

正文到此结束
Loading...