Android Basic Activity

本技术点学习指导说明

1.精通Activity的生命周期

2.熟悉Task的含义,掌握Activity与Task的关系

3.熟悉Activity的启动流程

4.了解ActivityManagerService如何管理Activity

5.总结Activity中出现的问题



Activity基本定义

Activity是Android应用程序的四大组件之一,用于管理Android应用程序的用户界面。一个应用程序可以含有多个Activity 组件,一个Activity组件管理一个界面,Activity可以运行在同一个进程中,也可以运行在不同的进程中,运行在不同进程中的Activity 是通过Binder进程间通信机制来进行协作,实现应用功能的。


Activity的生命周期

 先分享一张经典的生命周期流程图:

具体生命周期介绍如下:

1.启动Activity:系统会先调用onCreate方法,然后调用onStart方法,最后调用onResume,Activity进入运行状态。

2.当前Activity被其他Activity覆盖其上或被锁屏:系统会调用onPause方法,暂停当前Activity的执行。

3.当前Activity由被覆盖状态回到前台或解锁屏:系统会调用onResume方法,再次进入运行状态。

4.当前Activity转到新的Activity界面或按Home键回到主屏,自身退居后台:系统会先调用onPause方法,然后调用onStop方法,进入停滞状态。

5.用户后退回到此Activity:系统会先调用onRestart方法,然后调用onStart方法,最后调用onResume方法,再次进入运行状态。

6.当前Activity处于被覆盖状态或者后台不可见状态,即第2步和第4步,系统内存不足,杀死当前Activity,而后用户退回当前Activity:再次调用onCreate方法、onStart方法、onResume方法,进入运行状态。

7.用户退出当前Activity:系统先调用onPause方法,然后调用onStop方法,最后调用onDestory方法,结束当前Activity。


Activity在AndroidManifest中的配置属性

android:allowTaskReparenting

allowTaskReparenting用于配置是否允许该activity可以更换从属task,通常情况与taskAffinity连在一起使用,用于实现把一个应用程序的Activity移到另一个应用程序的Task中。

allowTaskReparenting用来标记Activity能否从启动的Task移动到taskAffinity指定的Task, 默认是继承至 application中的allowTaskReparenting=false,如果为true,则表示可以更换;false表示不可以。

引用网上的解释例子:

      一般来说,当Activity启动后,它就与启动它的Task关联,并且在那里耗尽它的整个生命周期。当当前的Task不再显示 时,你可以使用这个特性来强制Activity移动到有着affinity的Task中。例如,如果e-mail中包含一个web页的链接,点击它就会启 动一个Activity来显示这个页面。这个Activity是由Browser应用程序定义的,但是,现在它作为e-mail Task的一部分。如果它重新宿主到Browser Task里,当Browser下一次进入到前台时,它就能被看见,并且,当e-mail Task再次进入前台时,就看不到它了。

--------------------------------------------------------------------------------------
android:alwaysRetainTaskState

是否保留状态不变, 比如切换回home, 再从新打开, activity处于最后的状态

这个属性用于设置Activity所属的任务状态是否始终由系统来维护。如果设置为true,则由系统来维护状态,设置为false,那么 在某些情况下,系统会允许重设任务的初始状态。默认值是false。这个属性只对任务根节点的Activity有意义,其他所有的Activity都会被 忽略。通常,在某些情况中,当用户从主屏中重新启动一个任务时,系统会先清除任务(从堆栈中删除根节点Activity之上的所有Activity)。但 是,当这个属性被设置为true时,用户会始终返回到这个任务的最后状态,而不管中间经历了哪些操作。这样做是有好处的,例如,Web浏览器的应用就会保 留很多用户不想丢失的状态,如多个被打开的标签页。
--------------------------------------------------------------------------------------
android:clearTaskOnLanunch
比如 P 是 activity, Q 是被P 触发的 activity, 然后返回Home, 从新启动 P, 是否显示 Q
--------------------------------------------------------------------------------------
android:configChanges
当配置list发生修改时, 是否调用 onConfigurationChanged() 方法 比如 "locale|navigation|orientation".
--------------------------------------------------------------------------------------
android:enabled
activity 是否可以被实例化,
--------------------------------------------------------------------------------------
android:excludeFromRecents
是否可被显示在最近打开的activity列表里
--------------------------------------------------------------------------------------
android:exported
是否允许activity被其它程序调用
--------------------------------------------------------------------------------------
android:finishOnTaskLaunch
是否关闭已打开的activity当用户重新启动这个任务的时候
--------------------------------------------------------------------------------------
android.icon
--------------------------------------------------------------------------------------
android:label
--------------------------------------------------------------------------------------
android:launchMode
activity启动方式, "standard" "singleTop" "singleTask" "singleInstance"
其中前两个为一组, 后两个为一组
具体介绍文档链接如下:

Activity_的launchMode.doc
--------------------------------------------------------------------------------------
android:multiprocess
允许多进程
--------------------------------------------------------------------------------------
android:name
activity的类名, 必须指定
--------------------------------------------------------------------------------------
androidnHistory
是否需要移除这个activity当用户切换到其他屏幕时。 这个属性是 API level 3 中引入的
--------------------------------------------------------------------------------------
android:permission
--------------------------------------------------------------------------------------
android:process
一 个activity运行时所在的进程名,所有程序组件运行在应用程序默认的进程中,这个进程名跟应用程序的包名一致。中的元素process属性能够为所 有组件设定一个新的默认值。但是任何组件都可以覆盖这个默认值,允许你将你的程序放在多进程中运行。 如果这个属性被分配的名字以:开头,当这个activity运行时, 一个新的专属于这个程序的进程将会被创建。 如果这个进程名以小写字母开头,这个activity将会运行在全局的进程中,被它的许可所提供。
--------------------------------------------------------------------------------------
android:screenOrientation
activity显示的模式, "unspecified" 默认值 "landscape" 风景画模式,宽度比高度大一些 "portrait" 肖像模式, 高度比宽度大。 "user" 用户的设置 "behind" "sensor" "nosensor"
--------------------------------------------------------------------------------------
android:stateNotNeeded
是否 activity被销毁和成功重启并不保存状态
--------------------------------------------------------------------------------------
android:taskAffinity
activity的亲属关系, 默认情况同一个应用程序下的activity有相同的关系
--------------------------------------------------------------------------------------
android:theme
activity的样式主题, 如果没有设置,则activity的主题样式从属于应用程序, 参见元素的theme属性
--------------------------------------------------------------------------------------
android:windowSoftInputMode
activity主窗口与软键盘的交互模式, 自从API level 3 被引入

活动的主窗口如何与包含屏幕上的软键盘窗口交互。这个属性的设置将会影响两件事情:

1> 软键盘的状态——是否它是隐藏或显示——当活动(Activity)成为用户关注的焦点。

2> 活动的主窗口调整——是否减少活动主窗口大小以便腾出空间放软键盘或是否当活动窗口的部分被软键盘覆盖时它的内容的当前焦点是可见的。

它 的设置必须是下面列表中的一个值,或一个”state…”值加一个”adjust…”值的组合。在任一组设置多个值——多 个”state…”values,例如&mdash有未定义的结果。各个值之间用|分开。例如: <activity android:windowSoftInputMode="stateVisible|adjustResize" . . . >

在这设置的值(除"stateUnspecified"和"adjustUnspecified"以外)将覆盖在主题中设置的值。

值描述:
 
"stateUnspecified" 软键盘的状态(是否它是隐藏或可见)没有被指定。系统将选择一个合适的状态或依赖于主题的设置。这个是为了软件盘行为默认的设置。
 
"stateUnchanged" 软键盘被保持无论它上次是什么状态,是否可见或隐藏,当主窗口出现在前面时。
 
"stateHidden" 当用户选择该Activity时,软键盘被隐藏——也就是,当用户确定导航到该Activity时,而不是返回到它由于离开另一个Activity。
 
"stateAlwaysHidden" 软键盘总是被隐藏的,当该Activity主窗口获取焦点时。
 
"stateVisible" 软键盘是可见的,当那个是正常合适的时(当用户导航到Activity主窗口时)。
 
"stateAlwaysVisible" 当用户选择这个Activity时,软键盘是可见的——也就是,也就是,当用户确定导航到该Activity时,而不是返回到它由于离开另一个Activity。
 
"adjustUnspecified" 它不被指定是否该Activity主 窗口调整大小以便留出软键盘的空间,或是否窗口上的内容得到屏幕上当前的焦点是可见的。系统将自动选择这些模式中一种主要依赖于是否窗口的内容有任何布局 视图能够滚动他们的内容。如果有这样的一个视图,这个窗口将调整大小,这样的假设可以使滚动窗口的内容在一个较小的区域中可见的。这个是主窗口默认的行为 设置。
 
"adjustResize" 该Activity主窗口总是被调整屏幕的大小以便留出软键盘的空间。
 
"adjustPan" 该Activity主窗口并不调整屏幕的大小以便留出软键盘的空间。相反,当前窗口的内容将自动移动以便当前焦点从不被键盘覆盖和用户能总是看到输入内容 的部分。这个通常是不期望比调整大小,因为用户可能关闭软键盘以便获得与被覆盖内容的交互操作。


Activity之间数据传递

下文主要介绍了两种fragment与activity的通信方式。第一种是用Bundle,另一种是用Handler。

fragment与Activity通信


Activity的启动流程

相关文档链接如下:

Activity启动流程.doc

Activity与Task

  • Activity和Task(栈)的关系

         Task就像一个容器,而Activity就相当与填充这个容器的东西,第一个东西(Activity)则会处于最下面,最后添加的东西 (Activity)则会在最上面。从Task中取出东西(Activity)是从最顶端取出,也就是说最先取出的是最后添加的东西 (Activity),以此类推,最后取出的是第一次添加的Activity,而Activity在Task中的顺序是可以控制的,在Activity跳 转时用到Intent Flag可以设置新建activity的创建方式。


  • Affinity定义

          在某些情况下,Android需要知个Activity道一属于哪个Task,即使它没有被启动到一个具体的Task里。这是通过任务共用性 (Affinities)完成的。任务共用性(Affinities)为这个运行一个或多个Activity的Task提供了一个独特的静态名称,默认的 一个活动的任务共用性(Affinity)是实现了该Activity的.apk包的名字。

          当开始一个没有Intent.FLAG_ACTIVITY_NEW_TASK标志的Activity时,任务共用性affinities不会影响将会运行 该新活动的Task:它总是运行在启动它的Task里。但是,如果使用了NEW_TASK标志,那么共用性(affinity)将被用来判断是否已经存在 一个有相同共用性(affinity)的Task。如果是这样,   这项Task将被切换到前面而新的Activity会启动于这个Task的顶层。

          这种特性在您必须使用NEW_TASK标志的情况下最有用,尤其是从状态栏通知或桌面快捷方式启动活动时。结果是,当用户用这种方式启动您的应用程序时,它的当前Task将被切换到前台,而且想要查看的Activity被放在最上面。

          你可以在程序清单(Manifest)文件的应用程序application标签中为.apk包中所有的活动分配你自己的任务共用性Affinites,或者在活动标记中为各个活动进行分配。

          一些说明其如何使用的例子如下:


  1. 如果您的.apk包含多个用户可以启动的高层应用程序,那么您可能需要对用户看到的每个Activity(活动)指定不同的 affinities。一个不错的命名惯例是以附加一个以冒号分隔的字符串来扩展您的.apk包名。例如,“ com.android.contacts ”.apk可以有affinities:“com.android.contacts:Dialer”和“ com.android.contacts:ContactsList”。
  2.     如果您正在替换一个通知,快捷方式,或其他可以从外部发起的应用程序的“内部”活动,你可能需要明确设定您替代活动的taskAffinity和您准备替 代的应用程序一样。例如,如果您想替换contacts详细信息视图(用户可以创建并调用快捷方式),你得把taskAffinity设置成 “com.android.contacts”。


  • Task有关的Intent对象中设置的Flag

FLAG_ACTIVITY_BROUGHT_TO_FRONT 
    这个标志一般不是由程序代码设置的,如在launchMode中设置singleTask模式时系统帮你设定。

FLAG_ACTIVITY_CLEAR_TOP 
    如果设置,并且这个Activity已经在当前的Task中运行,因此,不再是重新启动一个这个Activity的实例,而是在这个Activity上方 的所有Activity都将关闭,然后这个Intent会作为一个新的Intent投递到老的Activity(现在位于顶端)中。 
    例如,假设一个Task中包含这些Activity:A,B,C,D。如果D调用了startActivity(),并且包含一个指向Activity B的Intent,那么,C和D都将结束,然后B接收到这个Intent,因此,目前stack的状况是:A,B。 
    上例中正在运行的Activity B既可以在onNewIntent()中接收到这个新的Intent,也可以把自己关闭然后重新启动来接收这个Intent。如果它的启动模式声明为 “multiple”(默认值),并且你没有在这个Intent中设置FLAG_ACTIVITY_SINGLE_TOP标志,那么它将关闭然后重新创 建;对于其它的启动模式,或者在这个Intent中设置FLAG_ACTIVITY_SINGLE_TOP标志,都将把这个Intent投递到当前这个实 例的onNewIntent()中。 
    这个启动模式还可以与FLAG_ACTIVITY_NEW_TASK结合起来使用:用于启动一个Task中的根Activity,它会把那个Task中任 何运行的实例带入前台,然后清除它直到根Activity。这非常有用,例如,当从Notification Manager处启动一个Activity。

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET 
    如果设置,这将在Task的Activity stack中设置一个还原点,当Task恢复时,需要清理Activity。也就是说,下一次Task带着 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记进入前台时(典型的操作是用户在主画面重启它),这个Activity和它之 上的都将关闭,以至于用户不能再返回到它们,但是可以回到之前的Activity。 
    这在你的程序有分割点的时候很有用。例如,一个e-mail应用程序可能有一个操作是查看一个附件,需要启动图片浏览Activity来显示。这个 Activity应该作为e-mail应用程序Task的一部分,因为这是用户在这个Task中触发的操作。然而,当用户离开这个Task,然后从主画面 选择e-mail app,我们可能希望回到查看的会话中,但不是查看图片附件,因为这让人困惑。通过在启动图片浏览时设定这个标志,浏览及其它启动的Activity在下 次用户返回到mail程序时都将全部清除。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 
    如果设置,新的Activity不会在最近启动的Activity的列表中保存。

FLAG_ACTIVITY_FORWARD_RESULT 
    如果设置,并且这个Intent用于从一个存在的Activity启动一个新的Activity,那么,这个作为答复目标的Activity将会传到这个 新的Activity中。这种方式下,新的Activity可以调用setResult(int),并且这个结果值将发送给那个作为答复目标的 Activity。

FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY 
    这个标志一般不由应用程序代码设置,如果这个Activity是从历史记录里启动的(常按HOME键),那么,系统会帮你设定。

FLAG_ACTIVITY_MULTIPLE_TASK 
    不要使用这个标志,除非你自己实现了应用程序启动器。与FLAG_ACTIVITY_NEW_TASK结合起来使用,可以禁用把已存的Task送入前台的 行为。当设置时,新的Task总是会启动来处理Intent,而不管这是是否已经有一个Task可以处理相同的事情。 
    由于默认的系统不包含图形Task管理功能,因此,你不应该使用这个标志,除非你提供给用户一种方式可以返回到已经启动的Task。 
    如果FLAG_ACTIVITY_NEW_TASK标志没有设置,这个标志被忽略。

FLAG_ACTIVITY_NEW_TASK 
    如果设置,这个Activity会成为历史stack中一个新Task的开始。一个Task(从启动它的Activity到下一个Task中的 Activity)定义了用户可以迁移的Activity原子组。Task可以移动到前台和后台;在某个特定Task中的所有Activity总是保持相 同的次序。 
    这个标志一般用于呈现“启动”类型的行为:它们提供用户一系列可以单独完成的事情,与启动它们的Activity完全无关。 
    使用这个标志,如果正在启动的Activity的Task已经在运行的话,那么,新的Activity将不会启动;代替的,当前Task会简单的移入前台。参考FLAG_ACTIVITY_MULTIPLE_TASK标志,可以禁用这一行为。 
    这个标志不能用于调用方对已经启动的Activity请求结果。

FLAG_ACTIVITY_NO_ANIMATION 
    如果在Intent中设置,并传递给Context.startActivity()的话,这个标志将阻止系统进入下一个Activity时应用 Acitivity迁移动画。这并不意味着动画将永不运行——如果另一个Activity在启动显示之前,没有指定这个标志,那么,动画将被应用。这个标 志可以很好的用于执行一连串的操作,而动画被看作是更高一级的事件的驱动。

FLAG_ACTIVITY_NO_HISTORY 
    如果设置,新的Activity将不再历史stack中保留。用户一离开它,这个Activity就关闭了。这也可以通过设置noHistory特性。

FLAG_ACTIVITY_NO_USER_ACTION 
    如果设置,作为新启动的Activity进入前台时,这个标志将在Activity暂停之前阻止从最前方的Activity回调的onUserLeaveHint()。 
    典型的,一个Activity可以依赖这个回调指明显式的用户动作引起的Activity移出后台。这个回调在Activity的生命周期中标记一个合适的点,并关闭一些Notification。 
    如果一个Activity通过非用户驱动的事件,如来电或闹钟,启动的,这个标志也应该传递给Context.startActivity,保证暂停的Activity不认为用户已经知晓其Notification。

FLAG_ACTIVITY_PREVIOUS_IS_TOP 
    If set and this intent is being used to launch a new activity from an existing one, the current activity will not be counted as the top activity for deciding whether the new intent should be delivered to the top instead of starting a new one. The previous activity will be used as the top, with the assumption being that the current activity will finish itself immediately.

FLAG_ACTIVITY_REORDER_TO_FRONT 
    如果在Intent中设置,并传递给Context.startActivity(),这个标志将引发已经运行的Activity移动到历史stack的顶端。 
    例如,假设一个Task由四个Activity组成:A,B,C,D。如果D调用startActivity()来启动Activity B,那么,B会移动到历史stack的顶端,现在的次序变成A,C,D,B。如果FLAG_ACTIVITY_CLEAR_TOP标志也设置的话,那么这 个标志将被忽略。

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

    If set, and this activity is either being started in a new task or bringing to the top an existing task, then it will be launched as the front door of the task. This will result in the application of any affinities needed to have that task in the proper state (either moving activities to or from it), or simply resetting that task to its initial state if needed.

FLAG_ACTIVITY_SINGLE_TOP 
    如果设置,当这个Activity位于历史stack的顶端运行时,不再启动一个新的。


常见问题,典型问题

1.生命周期混乱异常

java.lang.RuntimeException: Performing stop of activity that is not resumed

解决方案:需要具体分析。

2.TransactionTooLargeException异常

解决方案:解决办法是减少bundle传输的数据量(bundle 传递数据大概要小于0.5兆)


技术陷阱

1.Activity在某些情况下,例如崩溃,不会调用生命周期函数OnPause


经验分享

1.onStart,onStop是对应于Activity是否可见来说的,而onResume于onPause是从Activity是否位于前台来说的。

2.系统配置发生改变最常见的就是横竖屏切换了。默认情况下,横竖屏切换后由于系统配置发生了改变,Activity就会被重新创建,并且 会多调用一个 onSaveInstanceState来保存Activity的状态,Activity被重新创建后,会调用 onRestoreInstanceState来恢复一下数据,我们可以根据这个方法是否被调用来判断Activity是否被重建了。这两个方法系统组织 了一些View的恢复工作,比如用户输入的数据、焦点等。

3.如果我们想要系统资源发生改变以后不重新创建, 只需要在AndroidManifest.xml 中指定android:configChanges=”orientation|screenSize”。


posted @ 2017-05-12 10:10  奔跑的蚂蚁aa  阅读(618)  评论(0编辑  收藏  举报