androidactivity栈
⑴ 由从服务中启动activity——谈谈安卓的任务与栈
在安卓的服务中这样启动活动:
你会得到这样的错误:
你知道我对安卓的什么地方最为痴迷?是它应用之间的协作。怎么协作?依靠activity之间的协作。
这种协作是可以跨越应用的。我们从task谈起。
task最直观的是安卓第三个虚拟键所列出的那些就是任务。
以上这种功能的实现,要从task谈起,developer上,这样定义task:
翻译过来,大体意识就是task是一个具有栈结构的容器,用以执行一定特定的工作,它可以放置多个Activity实例。
启动一个应用,系统就会为之创建一个task,来放置根Activity。默认情况下,一个Activity启动另一个Activity时,两个Activity是放置在同一个task中的,后者被压入前者所在的task栈,当用户按下后退键,后者从task被弹出,前者又显示在幕前,特别是启动其他应用中的Activity时,两个Activity对用户来说就好像是属于同一个应用。
task可以分为系统task和task。这两者之间是互相独立的。
当我们运行一个应用时,按下Home键回到主屏,启动另一个应用,这个过程中,之前的task被转移到后台,新的task被转移到前台,其根Activity也会显示到幕前,过了一会之后,在此按下Home键回到主屏,再选择之前的应用,之前的task会被转移到前台,系统仍然保留着task内的所有Activity实例,而那个新的task会被转移到后台,如果这时用户再做后退等动作,就是针对该task内部进行操作了。
一个Activity当然要表面自己身在哪个task,所以每个Activity都有taskAffinity属性。这个属性指出了它希望进入的Task。
如果一个Activity指明自己的taskaffinity,那么它的这个属性就等于Application指明的taskAffinity,如果Application也没有指明,那么该taskAffinity的值就等于包名。
而Task也有自己的affinity属性,它的值等于它的根Activity的taskAffinity的值。
显示的声明activiy的属性
这很简单,在AndroidManifest.xml中声明
有了上面的基础,请记住这两种文档说明的情况:
1. 如果该Activity的allowTaskReparenting设置为true,它进入后台,当一个和它有相同affinity的Task进入前台时,它会重新宿主,进入到该前台的task中。
**2. 如果加载某个Activity的intent,Flag被设置成FLAG_ACTIVITY_NEW_TASK时,它会首先检查是否存在与自己taskAffinity相同的Task,如果存在,那么它会直接宿主到该Task中,如果不存在则重新创建Task。 **
activity有四种启动模式,分别为 standard,singleTop,singleTask,singleInstance。
这四种模式我不细说,我们只从名字上分析分析。
第一种,标准模式,想想就知道是平常的模式,这里的标准意思是每生成一个activity的实例,就当一个实例的放在栈里。
第二种,singleTop,在于那个top。要是activity不在栈顶,它和standard模式没什么区别,要是在top,就不创建一个新的,用栈顶原来那个。
第三种,不那么容易,尤其是 官网的说法好像有问题。
singleTask模式的Activity只允许在系统中有一个实例。 如果系统中已经有了一个实例,持有这个实例的任务将移动到顶部,同时intent将被通过onNewIntent()发送。如果没有,则会创建一个新的Activity并置放在合适的任务中。
这话的意思,我们分两种情况讨论,一是在同一个应用,二是不同。
在同一个应用中,如果系统中还没有singleTask的Activity,会新创建一个,并放在同一任务的栈顶。但是如果已经存在,singleTask Activity上面的所有Activity将以合适的方式自动销毁,让我们想要显示的Activity处于栈顶。
在非同一个应用中,intent是从另外的应用发送过来。系统中没有任何Activity的实例的化,会创建一个新的任务,并且新的Activity被作为根Activity创建;如果系统中拥有这个singleTask的应用存在,新建的Activity会置于这个任务的上面。
第四种,和第三种很像,关键在于singleInstance,就是只能有这一个单例存在在栈中。
回到开头,我们还没解决开始的错误。最简单粗暴的方法是:
这可以解决问题,不过也还存在一个小问题,留待读者发现:)
Flag的字面意思很好理解,如果你读懂了上述的任务与活动启动模式的化,再提供几个intent Flags:
至于意思,还是字面理解就好了。
⑵ Android基础之Activity 运行模式与回退栈
LaunchMode 定义的是activity实例与task之间的关系,可以通过下面的两种方式来定义:
在Activity A中启动B,可以利用Activity B在清单中的launchmode定义,也可以在A中调用startActivity()的时候通过intent的flag传入,当两种方式都有定义,intent的flag参数会覆盖掉B原有的定义。
利用Activity 元素的launchMode属性
launchMode属性指定Activity如何被运行到一个task中。launchMode的值有四种:
默认, 每次启动Activity系统都会产生一个新的实例,并且把intent发送给新产生的实例,这个Activity可以被实例化多次,每个实例可以属于不同的task,每个task也可以保有多个此Activity的实例。
如果当前task 的回退栈栈顶已经存在一个此Activity的实例,系统通过调用这个实例的onNewIntent()方法把intent发送给这个Activity实例,而不是创建一个新的此Activity的实例。这个Activity也可以被实例化多次,每个实例可以属于不同的task,每个task可一个保有多个实例(仅限于此Activity已存在的实例不在栈顶)
注意:
应用场合如下:不想出现2个同样的activity在顶部。比如用户正在一个activity阅读信息,这时来了notification,用户点击后应该更新这些信息,而不是新建一个activity,这样在点击back时,就不会出现回到旧信息activity的情况了。这种情况正是下面这段英语提到的。
Note: When a new instance of an activity is created, the user can press the Back button to return to the previous activity. But when an existing instance of an activity handles a new intent, the user cannot press the Back button to return to the state of the activity before the new intent arrived in onNewIntent()
.
例如,当前回退栈中有A,B,C,D四个Activity,全部是Standard,在D中调用startActivity()去启动B,intent的flag设置成FLAG_ACTIVITY_CLEAR_TOP 和FLAG_ACTIVITY_NEW_TASK,系统发现栈中有B,会先销毁这个B,再原位置重建B,清空CD,而不是把这个新建的B的实例压入栈顶,这里之所以会销毁B再新建B,因为B的launchmode是Standard,无论什么情况下启动,都需要new一个B的实例,但如果此时B是SingleTop的,系统会把这个intent通过onNewIntent传给已经在栈中的B的实例,不需要销毁再创建,仍需要清空CD。
系统会创建一个新的task并且把这个实例放在栈底( 此处有疑问,测试发现并不一定是栈底 ),但是,如果在一个单独的task中已经存在一个此Activity的实例,系统会把intent通过onNewIntent()发送给这个实例( 测试发现如果在回退栈中,该Activity的上面还有其他Activity,启动此Activity会清空栈中此Activity上面的其他Activity ),而不是创建一个新的实例。同一时间在只有一个此Activity的实例存在于系统中。
与SingleTask一样,不同的是SingleTask的Activity所在的task中可以有其他的Activity,而SingleInstance的Activity独占一个task,并且在整个系统中只有唯一的一个实例。由这个Activity启动的其他Activity都会在新的task中打开。
另一个例子,系统自带浏览器APP把浏览器Activity声明为SingleTask,通过在Activity标签里的launchMode进行指定,这意味着如果你发送一个intent启动浏览器,不管是为浏览器新开启一个task还是从浏览器已经在后台保有的task中启动浏览器,浏览器Activity与你的APP不在同一个task。
不管一个Activity是不是在一个新的task中启动,点击返回都会返回前一个Activity。不过,如果启动一个LaunchMode为singleTask的Activity,如果该Activity此时在一个处于后台的task中,整个task会变成前台task,此时,回退栈会包含由这个后台task携带过来所有Activity,放在回退栈的栈顶,下图说明这种情况。
在一个新的task里启动Activity. 如果已经有Activity实例运行在某一task中,启动这个Activity会把该实例所在的task带到前台,由该实例的onNewIntent()来接收新的intent。
如果被启动的Activity就是当前的Activity,这个已经存在的实例通过onNewIntent()接收intent,不会产生新的实例。
被启动的Activity如果已经存运行于当前task,回退栈中所有在此Activity上面的Activity都将被销毁,此Activity通过onNewIntent()接收新的intent。
例如,一个task中有A,B,C,D,四个Activity,如果D 调用startActivtiy()启动Activity B,C和D会被销毁,B接收这个intent,回退栈中有A,B。
上例中的Activity B的实例,或者通过onNewIntent()接收新的intent,或者销毁新建来处理新的intent。如果B的launchmode是standard,并且没有设置FLAG_ACTIVITY_SINGLE_TOP,那么B会被销毁重启,如果是其他launchmode或者设置了FLAG_ACTIVITY_SINGLE_TOP,则会通过onNewIntent()接收。
FLAG_ACTIVITY_CLEAR_TOP 和FLAG_ACTIVITY_NEW_TASK结合使用会有个不错的效果。
如果启动的Activity位于task的底部,它会把所在task带到前台,并且清理状态至root状态,当从通知栏里打开一个Activity的会非常有用。
Affinity指的是一个Activity偏向于从属于哪个task,默认情况下,一个APP内的所有Activity互相之间共享一个affinity的值,所以,所有同一APP下的所有Activity都偏向于从属于同一个task。但是,这个值是可以更改的,不同APP内的Activity可以共享一个affinity,同一个APP内的Activity也可以被分配不同的affinity的值。
affinity的值可以通过修改Activity标签的taskAffinity属性来修改。
这个属性接收一个String的值,必须在manifest标签范围内是唯一的值,因为系统是通过名称来标识APP的affinity的值的。
Affinity作用于以下两种情况:
通过startActivity()启动一个新的Activity时,默认情况下,新的Activity会被压入与启动者相同的回退栈中。但是,如果在启动Activity的时候,使用了FLAG_ACTIVITY_NEW_TASK 这个标志,系统会为新的Activity寻找一个新的task。通常情况下,是一个新的task。但是也并不是必须的。如果系统中有一个task的affinity值与新的Activity的值相同,新的Activity会被分配到这个task中。如果没有这样的task,就启动一个新的task。如果这个标志产生了一个新的task,当用户点击home键离开的时候,必须要有某种方式能够使用户返回到这个task来。有些实体(例如通知管理器)总是从一个外部task中启动Activity,所以在通过startActivity()启动新的Activity时总是需要传递FLAG_ACTIVITY_NEW_TASK 这个标志。如果你有一个Activity可以被外部实体可能这个标志启动,注意用户可以有一种独立的方式回到启动它的task,例如点击启动图标。
这种情况下,一个Activity可以动启动它的那个task移动到它的affinity值对应的task中,当那个task回到前台。例如,假设,一个报告指定城市天气情况的Activity作为一个旅行APP的一部分,它跟其他处在同一APP的Activity一样有一个相同的affinity值,并且允许通过这个属性来调整目标task。当你的一个Activity启动了这个天气预报Activity,它默认跟你的Activity在一个task里,但是,当旅行APP进入到前台,这个天气预报Activity又会被重新分配给旅行APP并且在旅行APP内展示。
提示:
如果一个APK文件从用户的角度看是多款APP,可能需要这个属性来设置不同的affinity来关联不同的APP。
如果用户离开一个task太长时间,系统会清除task中的所有Activity仅仅保留这下根Activity。当用户返回到这个task的时候,只有这个根Activity会被恢复。系统通过这种方式来处理,是因为经过相当长的一段时间之后,用户已经抛弃他们曾经正在做的事情,计划再回来的时候做点新的事情。
你可以通过如下属性来更改这种行为:
参考文献:
Tasks and Back Stack
<activity>
Android 阅读Tasks and Back Stack文章后的重点摘抄
⑶ android怎样将activity放入全局栈
Activity是Android程序的表现层。程序的每一个显示屏幕就是一个Activity。正在运行的Activity处在栈的最顶端,它是运行状态的。
当有新的Activity进入屏幕最上端时,原来的Activity就会被压入第二层。如果他的屏幕没有被完 全遮盖,那么他处于Paused状态,如果他被遮盖那么处于Stop状态。
不管处于任何一层,都可能在系统觉得资源不足时被强行关闭,当然关闭时栈底的程序最先被关闭。
譬如:当你在程序中调用 Activity.finish()方法时,结果和用户按下 BACK 键一样:他告诉 Activity Manager该Activity实例可以被“回收”。随后 Activity Manager 激活处于栈第二层的 Activity 并重 新入栈,把原 Activity 压入到栈的第二层,从 Running 状态转到 Paused 状态。
在BlackBerry中,提供了一个管理Screen的栈,用来从任何地方来关闭位于最上一层的Screen,使用UiApplication.getUiApplication().getActiveScreen()来得到位于最上一层的Screen的实例,并且使用UiApplication.getUiApplication().popScreen()来关闭一个Screen或关闭当前最上一层的Screen,但是Android却未提供相应的功能,只能在一个Activity的对象里面调用finish来关闭自己,不能关闭其他的Activity。比如我们想实现一个功能从屏幕A—>屏幕B—>屏幕C—>屏幕D,然后在在转到屏幕D之前将屏幕B和C关闭,在屏幕B和屏幕C界面点击会退按钮都可以回退到上一个屏幕,但是在屏幕D上点击会退按钮让其回退到A,此外在一些循环跳转的界面上如果不在合适的地方将一些不需要的屏幕关闭,那么经过多次跳转后回导致内存溢出。对此我们可以设计一个全局的Activity栈,使用这个栈来管理Activity。管理Activity的类的定义如下:
import java.util.Stack;
import android.app.Activity;
public class ScreenManager {
private static Stack activityStack;
private static ScreenManager instance;
private ScreenManager(){
}
public static ScreenManager getScreenManager(){
if(instance==null){
⑷ android 中怎样能够清除activity堆栈,也就是退出整个应用
如果退出整个程序,如下操作:方式一:Intent intent=new Intent(Intent.ACTION_MAIN);intent.addCategory(Intent.CATEGORY_HOME);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);this.startActivity(intent);System.exit(0); 方式二:android.os.Process.killProcess(android.os.Process.myPid()); android 完全退出程序有几个activity,有一需求是在一个activityA点击back键退出系统而不是跳到之前的activity首先想到的是清空activityA的堆栈,使用intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 但是该activityA不是已经存在于堆栈底端的,所以清除的只是堆栈中该activityA上面的activity,但后退后还是会返回堆栈中该activityA下面的activity。
⑸ android a应用的activity启动b应用的activity为什么在同一个任务栈
在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作。在Android中Activity的启动模式决定了Activity的启动运行方式。
Android总Activity的启动模式分为四种:
Activity启动模式设置:
<activity android:name=".MainActivity" android:launchMode="standard" />
Activity的四种启动模式:
1. standard
模式启动模式,每次激活Activity时都会创建Activity,并放入任务栈中。
2. singleTop
如果在任务的栈顶正好存在该Activity的实例, 就重用该实例,否者就会创建新的实例并放入栈顶(即使栈中已经存在该Activity实例,只要不在栈顶,都会创建实例)。
3. singleTask
如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的onNewIntent())。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移除栈。如果栈中不存在该实例,将会创建新的实例放入栈中。
4. singleInstance
在一个新栈中创建该Activity实例,并让多个应用共享改栈中的该Activity实例。一旦改模式的Activity的实例存在于某个栈中,任何应用再激活改Activity时都会重用该栈中的实例,其效果相当于多个应用程序共享一个应用,不管谁激活该Activity都会进入同一个应用中。
其中standard是系统默认的启动模式。
下面通过实例来演示standard的运行机制:
1 private TextView text_show;
2 private Button btn_mode;
3
4 @Override
5 public void onCreate(Bundle savedInstanceState) {
6 super.onCreate(savedInstanceState);
7 setContentView(R.layout.activity_main);
8
9 text_show = (TextView) this.findViewById(R.id.text_show);
10
11 text_show.setText(this.toString());
12
13 btn_mode = (Button) this.findViewById(R.id.btn_mode);
14
15 }
16
//按钮单击事件
17 public void LaunchStandard(View v){
18 startActivity(new Intent(this,MainActivity.class));
19
20 text_show.setText(this.toString());
21 }
⑹ Android中的Activity详解--启动模式与任务栈
目录
activity的简单介绍就不写了,作为最常用的四大组件之一,肯定都很熟悉其基本用法了。
首先,是都很熟悉的一张图,即官方介绍的Activity生命周期图.
情景:打开某个应用的的FirstActivity调用方法如下:
由于之前已经很熟悉了,这里就简单贴一些图。
按下返回键:
重新打开并按下home键:
再重新打开:
在其中打开一个DialogActivity(SecondActivity)
按下返回:
修改SecondAcitvity为普通Activity,依旧是上述操作:
这里强调一下 onSaveInstanceState(Bundle outState) 方法的调用时机:
当Activity有可能被系统杀掉时调用,注意,一定是被系统杀掉,自己调用finish是不行的。
测试如下:FirstActivity启动SecondActivity:
一个App会包含很多个Activity,多个Activity之间通过intent进行跳转,那么原始的Activity就是使用栈这个数据结构来保存的。
Task
A task is a collection of activities that users interact with when performing a certain job. The activities are arranged in a stack (the back stack ), in the order in which each activity is opened.
即若干个Activity的集合的栈表示一个Task。
当App启动时如果不存在当前App的任务栈就会自动创建一个,默认情况下一个App中的所有Activity都是放在一个Task中的,但是如果指定了特殊的启动模式,那么就会出现同一个App的Activity出现在不同的任务栈中的情况,即会有任务栈中包含来自于不同App的Activity。
标准模式,在不指定启动模式的情况下都是以此种方式启动的。每次启动都会创建一个新的Activity实例,覆盖在原有的Activity上,原有的Activity入栈。
测试如下:在FirstActivity中启动FirstActivity:
当只有一个FirstActivity时堆栈情况:
此种模式下,Activity在启动时会进行判断,如果当前的App的栈顶的Activity即正在活动的Activity就是将要启动的Activity,那么就不会创建新的实例,直接使用栈顶的实例。
测试,设置FirstActivity为此启动模式,多次点击FirstActivity中的启动FirstActivity的按钮查看堆栈情况:
(其实点击按钮没有启动新Activity的动画就可以看出并没有启动新Activity)
大意就是:
对于使用singleTop启动或Intent.FLAG_ACTIVITY_SINGLE_TOP启动的Activity,当该Activity被重复启动(注意一定是re-launched,第一次启动时不会调用)时就会调用此方法。
且调用此方法之前会先暂停Activity也就是先调用onPause方法。
而且,即使是在新的调用产生后此方法被调用,但是通过getIntent方法获取到的依旧是以前的Intent,可以通过setIntent方法设置新的Intent。
方法参数就是新传递的Intent.
1.如果是同一个App中启动某个设置了此模式的Activity的话,如果栈中已经存在该Activity的实例,那么就会将该Activity上面的Activity清空,并将此实例放在栈顶。
测试:SecondActivity启动模式设为singleTask,启动三个Activity:
这个模式就很好记,以此模式启动的Activity会存放在一个单独的任务栈中,且只会有一个实例。
测试:SecondActivity启动模式设为singleInstance
结果:
显然,启动了两次ThirdActivity任务栈中就有两个实例,而SecondActivity在另外一个任务栈中,且只有一个。
在使用Intent启动一个Activity时可以设置启动该Activity的启动模式:
这个属性有很多,大致列出几个:
每个启动的Activity都在一个新的任务栈中
singleTop
singleTask
用此种方式启动的Activity,在它启动了其他Activity后,会自动finish.
官方文档介绍如下:
这样看来的话,通俗易懂的讲,就是给每一个任务栈起个名,给每个Activity也起个名,在Activity以singleTask模式启动时,就检查有没有跟此Activity的名相同的任务栈,有的话就将其加入其中。没有的话就按照这个Activity的名创建一个任务栈。
测试:在App1中设置SecondActivity的taskAffinity为“gsq.test”,App2中的ActivityX的taskAffinity也设为“gsq.test”
任务栈信息如下:
结果很显然了。
测试:在上述基础上,在ActivityX中进行跳转到ActivityY,ActivityY不指定启动模式和taskAffinity。结果如下:
这样就没问题了,ActivityY在一个新的任务栈中,名称为包名。
这时从ActivityY跳转到SecondActivity,那应该是gsq.test任务栈只有SecondActivity,ActivityX已经没有了。因为其启动模式是singleTask,在启动它时发现已经有一个实例存在,就把它所在的任务栈上面的Activity都清空了并将其置于栈顶。
还有一点需要提一下,在上面,FirstActivity是App1的lunch Activity,但是由于SecondActivity并没有指定MAIN和LAUNCHER过滤器,故在FirstActivity跳转到SecondActivity时,按下home键,再点开App1,回到的是FirstActivity。
大致就先写这么多吧,好像有点长,废话有点多,估计也有错别字,不要太在意~~~
⑺ android面试一个应用的两个activity 可以在不同栈里吗
1、什么是Activity
通俗一点说Activity就是一个界面,这个界面里面可以放置各个控件。Activity的界面也是用xml表示,放置在res->layout下面。每生成一个新的Activity后,我们需要在AndroidManifest.xml中注册一下这个Activity。
2、请描述一下Activity的生命周期
onCreate(Bundle savedInstanceState):创建Activity时调用,设置在该方法中,还以Bundle的形式提供对以前存储的任何状态的访问。
onStart():activity变为在屏幕上对用户可见时调用
onResume():activity开始与用户交互时调用(无论是启动还是重新启动一个activity,该方法总是被调用)
onPause():activity被暂停或者收回cpu和其他资源时调用,该方法用于保存活动状态的,也是保护现场,压栈吧
onStop():activity被停止并转为不可见阶段及后续的生命周期事件时调用。
onRestart():重新启动activity时调用,该活动仍在栈中,而不是启动新的activity
onDestroy():activity被完全从系统内存中移除时调用
3、两个Activity之间跳转时必然会执行的是哪几个方法:
onCreate():在activity生命周期开始时调用
onRestoreInstanceState():用来恢复UI状态
onRestart():当activity重新启动时调用
onStart():当activity对用户即将可见时调用
onResume():当activity与用户交互时,绘制界面
onSaveInstanceState():当activity即将移除栈顶保留UI状态时被调用
onPause():暂停当前活动的activity,提交持久数据的改变,停止动画和其他占用CPu资源的东西,由于下一个activity在这个方法返回之前不会resume,所以这个方法的代码执行要快
onStop():activity不再可见时调用
onDestroy():在activity销毁栈时被调用的最后一个方法
4、横竖屏幕切换时候activity的生命周期
①不设置activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,且竖屏时会执行两次
②设置activity的android:configChanges=”orientation"时,切屏还是会调用各个生命周期,切横屏,竖屏时只会执行一次
③设置activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
5、你后台的Activity被系统回收怎么办?
系统会帮我们记录下回收之前Activity的状态,再次调用被回收的Activity就要重新调用onCreate()方法,不同于直接启动的是这回onCreate()里面带上参数savedInstanceState,savedInstanceState是一个Bundle对象,基本上可以把它理解为系统帮你维护的一个map对象,使用它可以恢复到回收前的状态
6、如何将一个activity设置为窗口的样式
①在你的style.xml文件中可以新建一如下的类似Dialog的style:
<style name="Theme.FloatActivity" parent="android:style/Theme.Dialog></style>
②在AndroidManifest.xml中在你需要显示为窗口的activity中添加如下属性:
android:theme="@style/Theme.FloatActivity"即可
也可以直接添加对应需要展示为Dialog style的activity的android:theme属性值为:android:theme="@android:style/Theme.Dialog"
7、两个activity之间怎样传递数据
在Intent的对象中增加要传递的参数即可
在Intent对象的请求中加入键值对,对应名字.putExtra(“键值对的名字","键值对的值");在另一个activity中将Intent的请求中的数据取出来:
Intent intent= getIntent();String value = intent.getStringExtra("testIntent")
8、在同一个程序,但不同的activity中是否可以放在不同的Task任务栈中?
可以放在不同的Task中。需要为不同的activity设置不同的taskaffinity属性,启动activity的Intent需要包含FLAG_ACTIVITY_NEW_TASK标记