目录
公司主营业务:网站设计制作、成都网站建设、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。创新互联是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。创新互联推出萨尔图免费做网站回馈大家。
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。
大致就先写这么多吧,好像有点长,废话有点多,估计也有错别字,不要太在意~~~
Activity是 Android组件 中最基本也是最为常见用的四大组件(Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器)之一 。
Activity是一个应用程序 组件 ,提供一个 屏幕 ,用户可以用来交互为了完成某项任务。
Activity中所有操作都与用户密切相关,是一个负责与 用户交互 的组件,可以通过setContentView(View)来 显示指定控件 。
在一个android应用中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应。Activity之间通过Intent进行通信。
关于Activity启动流程请参考之前的文章 Android activity启动流程分析
activity有四种启动模式,分别为standard,singleTop,singleTask,singleInstance。如果要使用这四种启动模式,必须在manifest文件中activity标签中的launchMode属性中配置。
标准的默认启动模式,这种模式下activity可以被多次实例化,即在一个task中可以存在多个activity,每一个activity会处理一个intent对象,(在A中再次启动A,会存在后面的A在前面的A上面,当前task会存在两个activity的实例对象)
如果一个singleTop模式启动的activity实例已经存在于栈顶,那么再次启动这个activity的时候,不会重新创建实例,而是重用位于栈顶的那个实例,并且会调用实例的onNewIntent()方法将Intent对象传递到这个实例中,如果实例不位于栈顶,会创建新的实例。
启动模式设置为singleTask,framework在启动该activity时只会把它标示为可在一个新任务中启动,至于是否在一个新任务中启动,还要受其他条件的限制,即taskAffinity属性。
taskAffinity :默认情况下,一个应用中的所有activity具有相同的taskAffinity,即应用程序的包名。我们可以通过设置不同的taskAffinity属性给应用中的activity分组,也可以把不同的应用中的activity的taskAffinity设置成相同的值,当两个不同应用中的activity设置成相同的taskAffinity时,则两个activity会属于同一个TaskRecord。
在启动一个singleTask的Activity实例时,如果系统中已经存在这样一个实例,就会将这个实例调度到任务栈的栈顶,并清除它当前所在任务中位于它上面的所有的activity;如果这个已存在的任务中不存在一个要启动的Activity的实例,则在这个任务的顶端启动一个实例;若这个任务不存在,则会启动一个新的任务,在这个新的任务中启动这个singleTask模式的Activity的一个实例。
以singleInstance模式启动的Activity具有全局唯一性,即整个系统中只会存在一个这样的实例,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。
以singleInstance模式启动的Activity具有独占性,即它会独自占用一个任务,被他开启的任何activity都会运行在其他任务中(官方文档上的描述为,singleInstance模式的Activity不允许其他Activity和它共存在一个任务中)。
被singleInstance模式的Activity开启的其他activity,能够开启一个新任务,但不一定开启新的任务,也可能在已有的一个任务中开启,受条件的限制,这个条件是:当前系统中是不是已经有了一个activity B的taskAffinity属性指定的任务。
涉及到Activity启动,就不得不说一下Activity的管理,Activity是以什么方式及被什么类来进行管理的,涉及的类主要如下:
历史栈中的一个条目,代表一个activity。ActivityRecord中的成员变量task表示其所在的TaskRecord,ActivityRecord与TaskRecord建立了联系。
内部维护一个 ArrayListActivityRecord 用来保存ActivityRecord,TaskRecord中的mStack表示其所在的ActivityStack,TaskRecord与ActivityStack建立了联系。
内部维护了一个 ArrayListTaskRecord ,用来管理TaskRecord,ActivityStack中持有ActivityStackSupervisor对象,由ActivityStackSupervisor创建。
负责所有ActivityStack的管理。内部管理了mHomeStack、mFocusedStack和mLastFocusedStack三个Activity栈。其中,mHomeStack管理的是Launcher相关的Activity栈;mFocusedStack管理的是当前显示在前台Activity的Activity栈;mLastFocusedStack管理的是上一次显示在前台Activity的Activity栈。
ActivityThread 运行在UI线程(主线程),App的真正入口。
用来实现AMS和ActivityThread之间的交互。
负责调用Activity和Application生命周期。
当一个Activity未被主动关闭,即“被动关闭”时,可能需要系统给用户提供保持一些状态的入口。
前面说的入口就是:Activity提供了onSaveInstanceState()方法,该方法是Activity在关闭前保存状态的核心方法。
前面提到“被动关闭”,如果是主动关闭那么就不会调用,比如:按back键、调用finish()等,那么"被动关闭"的场景有哪些呢?下面给列举一下:
肯定在调用onStop()前被调用,但不保证在onPause()前 / 后,一般是在onPause()后调用。
当需要保持状态时,在onSaveInstanceState()内执行以下逻辑:
当需要恢复时,在onCreate()内部执行以下逻辑:
布局每个View默认实现:onSaveInstanceState(),即UI的任何改变都会自动的存储和在activity重新创建的时候自动的恢复(只有在为该UI提供了唯一ID后才起作用);
若需复写该方法从而存储额外的状态信息时,应先调用父类的onSaveInstanceState()(因为默认的onSaveInstanceState()帮助UI存储它的状态);
只使用该方法记录Activity的瞬间状态(UI的状态),而不是去存储持久化数据,因为onSaveInstanceState()调用时机不确定性;可使用 onPause()[一定会执行]存储持久化数据;
Activity提供了onRestoreInstanceState()方法,该方法是Activity在重新创建后恢复之前保存状态的核心方法。
若被动关闭了Activity,即调用了onSaveInstanceState(),那么下次启动时会调用onRestoreInstanceState()。
onCreate()---onStart()---onRestoreInstanceState()---onResume()
注意: onSaveInstanceState()、onRestoreInstanceState()不一定 成对被调用
如:当正在显示Activity A时,用户按下HOME键回到主界面,然后用户紧接着又返回到Activity A,此时Activity A一般不会因为内存的原因被系统销毁,故Activity A的onRestoreInstanceState()不会被执行;
针对以上情况,onSaveInstanceState保持的参数可以选择在onCreate()内部进行解析使用,因为onSaveInstanceState的bundle参数会传递到onCreate方法中,可选择在onCreate()中做数据还原。
至此:Activity的启动模式及Activity的状态保持及恢复介绍完毕。
singleTop 与 singleTask 是 Activity 最常用的两种启动模式。本文主要讲解两者之间的区别与使用场景。
个人博客: 李益的小站
Activity 共有四种启动模式,我们先简单回顾一下,如想要详细了解的,可自行网上查询(相关文章很多哦,本文就不再详细赘述)了。
使用 singleTop 模式的 Activity 在栈顶时只会在 Task 中存在一个实例,所以可以在以下场景中使用:
总之, singleTop 比较适用于 ChildActivity (非主架构Activity)
所以基于以上特性,比较适合主架构 Activity (例MainActivity)设置为 singleTask ,或者一些经常使用,但是关闭和跳转不规律的 Activity
standard(默认)
系统默认的启动模式。
Android是使用返回栈来管理活动的,在standard模式下,每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置。
对于使用standard模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,而是每次启动活动都会创建该活动的一个新的实例。
singleTop
android:launchMode="singleTop"
当活动的启动模式指定为singleTop,在启动活动时,如果发现该返回栈的栈顶已经是该活动时,则认为可以直接使用它,不会在创建新的活动实例
singleTask
当活动的启动模式指定为singleTask,每次启动该活动时,首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在就直接使用该实例,并把这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。
android:launchMode="singleTask"
singleInstance
指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用的同一个返回栈,解决了共享活动实例的问题
修改SecondActivity的启动模式
android:launchMode="singleInstance"
使用方式:
standard:怎么样都要创建
singleTop:顶上不是target Activity,new一个
singleTask:顶上不是target Activity,移除target之上的,把自己变成top。
singleInstance:开辟私有的task,完全独立于程序的其他activity的task。
使用场景:
standard:普通activity
singleTop:要展示推送过来的消息
singleTask:程序入口等启动页面
singleInstance:完全独立的,类似闹钟的提示
最近遇到了一个小问题,在我使用了多种Activity启动模式的时候,重新打开其中的一个Activity会启动另一个我已经停止的Activity,从而调用了一些已经失效的方法导致程序崩溃。
由于项目工程复杂,Activity名称不够直观,我新建了一个ActivityTaskTest 工程来重现遇到的问题。
ActivityA是工程的主活动。因为一些必要的原因, ActivityA的启动模式是SingleInstance的。ActivityA可以启动ActivityB,ActivityB没有设置任何启动模式,即默认的standard启动模式。在ActivityB中,将会启动一个ServiceA。 ServiceA中启动一个了一个ActivityC,由于Activity是在非Acitivity环境下启动的,需要设置 FLAG_ACTIVITY_NEW_TASK标签(这里就是我们讨论的重点,稍候会详细分析)。当ActivityC完成任务后会重新跳转到ActivityA。
最后,见证奇葩的时刻到了,我们点击ActivityA的启动ActivityB的button,ActivityC出现在了我们的眼前,而不是ActivityB!!这一刻我仿佛刘谦附体,但在我发现我身边并没有董卿之后,我深刻地意识到了我是一个工程师,不能搞这些装神弄鬼的事情。ok,Let's find out what‘s going on with our precious app!
关于Activity启动模式和Activity Task的内容推荐一篇非常好的文章: Android中Activity四种启动模式和taskAffinity属性详解 。这篇文章已经讲得非常详细了,这里就不再赘述了,偷个懒哈哈。
如果你已经看了文章,你应该已经知道问题的所在了,对,就是这个该死的taskAffinity。简单的说,就是我们虽然使用了FLAG_ACTIVITY_NEW_TASK标签去启动了ActivityC,但是因为我们忘了给Activity设置taskAffinity这个小婊砸,所以导致ActivityC的taskAffinity值和ActivityB一样都是默认的包名。所以我们启动ActivityC的时候系统将ActivityC压入了ActivityB所在的task。我们可以使用adb shell dumpsys activity activities 指令看下一在我们重新从A中启动B之前,Task的情况:
我们可以看到正如我们所想的,ActivityC和ActivityB在一个Task中,由于ActivityA是singleInstance模式,所以A只能做一辈子单身狗了。那么为什么我们明明启动的是B,怎么会出现C呢?
我们来先看看Google官方文档对于FLAG_ACTIVITY_NEW_TASK是怎么说的:
注意文档中的内容,“如果要启动的 activity 已经运行于某 task 中,则那个 task 将调入前台,最后保存的状态也将恢复”,注意这里是所在task被直接调入前台,也就是说B所在的整个Task将被移入前台。这就解释了为什么我们去启动B而出现的是C了。看起来我们好像大功告成了,但是,等等,总觉得哪里有点不太对劲,我们的ActivityB明明没有设置启动模式啊,你这个是FLAG_ACTIVITY_NEW_TASK标签,我没用啊,我读书多你可别骗我。
仔细想想应该是ActivityA的singleInstance的锅。
我们再来看看Google官方文档对于singleInstance是怎么说的吧:
看到最后一句,终于可以结案了。也就是说,当一个被设置为singleInstance的Activity去启动其他的Activity的时候,其默认是自带FLAG_ACTIVITY_NEW_TASK标签的。
1、FLAG_ACTIVITY_NEW_TASK标签必须配合taskAffinity属性使用,如果不设置taskAffinity属性值,将不会生成新task。
2、当从启动模式为singleInstance的Acitivity中启动新的Acitivity时,新的Activity自带FLAG_ACTIVITY_NEW_TASK标签。
心得:官方文档是个好东西。