为何须要了解关于Activity的任务栈,其实最直接的体现就是提升用户交互友好性。android
举个例子,当咱们去浏览一个新闻客户端的时候,咱们进入了新闻详情页,在这个页面有相隔两条的新闻标题,当咱们去点击这个标题的时候进入了新的新闻详情页时,若是咱们不加以控制会致使什么现象?它会建立出n个新闻详细页的Activity实例,致使用户在退出的时候须要推出多个新闻详情activity,这点在用户体验上是很是很差的,固然对于咱们自身的程序也是很是很差的,不断的去建立新的Activity一定会消耗必定的内存,长此以往,应用程序会愈来愈卡甚至崩溃。
app
在讲Activity任务栈前,咱们应该先知道什么是栈?ide
简单点来理解,能够把栈比做一个开封的箱子,咱们能够往里面塞东西,这里假设塞的东西的底面积和箱子的底面积是相同的,那么这些东西就具有有从下往上必定的顺序,当咱们想要取出箱子里面的东西时,咱们没有办法一会儿拿到箱子最底层的东西,咱们只能拿到最上面一层的东西,从上往下。this
来看下这张图,这里的箱子就是栈,箱子口能够看做是栈的入口与出口,东西表明数据。栈的特色:具备必定的次序,后进先出(越先放入的东西,越晚出来)。spa
一、Activity任务栈 设计
好了,在了解了什么是栈以后,咱们能够开始进入今天的主题了,在Android的官方文档描述中咱们能够知道,任务栈也是栈,具备栈的一切特色。3d
Activity任务栈,顾名思义是存放Activity任务的栈,这里的任务栈为上图箱子,Activity为上图的东西。日志
当咱们每打开一个Activity的时候它会就往Activity任务栈中压入一个Activity,当咱们每销毁一个Activity的时候它会从Activity任务栈中弹出一个Activity,因为安卓系统自身的设计,咱们只能在手机屏幕上获取当前一个Activity的焦点即栈顶元素(最上面的Activity),其他的Activity会暂居后台等待系统调用。code
1.一、关于任务栈的概念:xml
1.二、关于任务栈的缺点:
二、Activity的4种启动方式
为了解决任务栈产生的问题,Android为Activity设计了启动模式,那么下面的内容将介绍Android中Activity的启动模式,这也是最重要的内容之一。
启动模式(launchMode)在多个Activity跳转的过程当中扮演着重要的角色,它能够解决是否生成新的Activity实例,是否重用已经存在的Activity实例,是否和其余实例共用一个任务栈。任务栈是一个具备栈结构的对象,一个任务栈能够管理多个Activity,每启动一个应用,也就建立一个与之对应的任务栈。
<activity>
的android:launchMode属性为以上四种之一便可。
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 }
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 }
实验一:启动模式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的按钮,让它跳转自身,看下当前任务栈:
当咱们按下Back键,跳转第一个Activity,销毁第二个Activity时,看下当前任务栈:
能够发现,这个任务栈和咱们刚上图描述的箱子(Activity任务栈)是一致的,从上往下放东西(Activity),越晚放进去的东西(Activity)在越上面,然后面的t1072则表明当前任务栈的编号ID,ID相同表明它们属于同一个任务栈。
咱们从日志文件中也能够看得很清楚:
实验一结论:
在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启动模式为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启动模式为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,看下当前任务栈:
系统任务:
点击Back键,直到程序彻底退出,看下系统日志:
实验四结论:
在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 }
一、在standard(默认)启动模式下,咱们来回的去跳转Activity,看下日志打印,发现是不会调用onNewIntent方法的,由于它不是一个实例。
二、在singleTop模式下,咱们从第一个Activity跳转到第二个Activity,再从第二个Activity跳转自身,再跳转第一个Activity,看下日志打印,咱们能够发现,当第二个Activity置于栈顶的时候,因为重用了实例,因此调用了onNewIntent方法。
三、当singleTask和singleInstance模式下也是同样的,由于重用了实例,因此会调用onNewIntent方法,且onNewIntent方法是在前一个Activity的onStop方法后(当前ActivityonReStart方法前)当即调用的。