开发艺术探索——第一章:Activity的生命周期和启动模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lhk147852369/article/details/84619610

目录

1.1 Activity 的生命周期和启动模式

1.1.1 典型情况下的生命周期分析

1.1.2 异常情况下的生命宁周期分析

1.2 Activity 的启动模式

1.2.1 Activity 的 LaunchMode

1.2.2 Activity 的Flags

1.3 IntentFilter 的匹配规则


知识点:

引言:本章的重点是Activity 在使用过程中的一些不容易搞清楚的概念,主要包括生命周期和启动模式以及IntentFilter的匹配规则分析。

1.1 Activity 的生命周期和启动模式

本章将Activity的生命周期分为两部分内容,一部分是典型情况下的生命周期,另一部分是异常情况下的生命周期。

1.1.1 典型情况下的生命周期分析

对于Activity的生命周期 相信大家已经是 牢记于心,对此,不在介绍每个方法所涵盖的内容了。

请继续往下看。

分析几种常见情况:

  1. 针对一个特定的Activity,第一次启动,回调如下:onCreate - onStart - onResume
  2. 当用户打开新的Activity或者切换到主界面时,回调如下:onPause - onStop,这里有一种特殊的情况,如果新Activity 采用了透明主题,那么当前Activity 不会回调onStop
  3. 当用户再次回到原Activity时,回调如下:onRestart - onStart - onResume
  4. 当用户按back 键回退时,回调如下:onPause - onStop - onDestory
  5. Activity被系统回收后再次打开 生命周期和 1 一样,不代表所有过程都一样
  6. 从整个生命周期来说 onCreate 和 onDestory 是配对的,从是否可见来说 onStart 和 onStop 是配对的 ,从Activity 是否在前台来说 onResume 和 onPause 是配对的。

问题:

Ask: onStart 和 onResume 、onPause 和 onStop 从描述来说差不多,对我们来说有什么实质的不同?

answer:onStart 和 onResume 是从Activity 是否可见这个角度来回调的,而onPause 和 onStop 是否在前台这个角度回调的,在实际使用中没有其他明显的区别。

Ask:当从A打开 B,那么B 的onResume 和 A 的onPause 哪个先执行呢?

answer:书中分析了源码,感兴趣的可以一观。 确定为 A 的onPause 先执行。

周期:A- onPause  B-onCreate  B- onStart B - onResume A- onStop

Activity的启动过程 简单理解: 启动Activity 的请求会由 Instrumentation 来处理,然后通过它的Binder向AMS发请求,AMS内部维护着一个ActivityStack并负责栈内的 Activity 的状态同步,AMS通过ActivityThread 去同步Activity的状态从而完成生命周期的回调。

1.1.2 异常情况下的生命宁周期分析

情况1:资源相关的系统配置发生了改变导致Activity 被杀死并重新创建。

比如横竖屏切换时,默认情况下,Activity就会被销毁并且重建,如果不作处理 生命周期如下:

onSaveInstanceState - onDestory 重新创建 onCreate onRestoreInstanceState

onSaveInstanceState  发生在onStop之前 他和onPause 没有既定的时序关系。

情况2:资源内存不足导致低优先级的Activity 被杀死

数据存储和恢复过程和情况 1 完全一致。

当资源内存不足时 将会先杀掉 优先级低 的Activity。

配置Activity相关属性,使Activity 在某些情况下不会重新创建

如 configChanges 属性添加 android:configChanges="orientation" 可避免 Activity 在屏幕旋转时重新创建。

若想指定多个值,可用“|”连接起来,如 andorid="orientation|keyboardHidden"

以下为大多数配置内容

常用的只有 locale、orientaion和keyboardHidden 这三个选项,需要注意的是screenSize 和 smallestScreenSize,它们两个比较特殊,它们的行为和编译选项有关,但和运行环境无关。

1.2 Activity 的启动模式

1.2.1 Activity 的 LaunchMode

standard:标准模式,没启动一个Activity 都会重新创建一个新的实例,在这种模式下,谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity所在的栈中。 注意:由于非Activity的Context(如ApplicationContext)并没有所谓的任务栈,所以就有问题了。解决办法:指定 FLAG_ACTIVITY_NEW_TASK 标记位。

singleTop:栈顶复用模式。如果新创建的 Activity 在栈顶,那么此Activity不会被重新创建,同时它的onNewIntent方法会被重新调用,周期:onCreate、onStart不会调用。

singleTask:栈内复用模式。这是一种单实例模式,只要Activity在一个栈中存在,那么将和singleTop一样,不会被重新创建,也会回调onNewIntent。同时会导致所有在此Activity之上的Activity全部出栈。

singleInstance:一种加强的 singleTask 模式,具有 singleTask 模式的所有特性,并且具有此种模式的 Activity 只能单独地位于一个任务栈中。

Activity 指定启动模式有两种方法:

通过 AndroidManifest.xml为 Activity 指定启动模式

通过在 Intent 中设置标志位为 Activity 指定启动模式。

二者区别在于:

优先级上,方式 2 的优先级高于方式 1,当两种同时存在时,以方式 2 为准;

限定范围不同,比如,方式 1 无法直接为 Activity 设定 FLAG_ACTIVITY_CLEAR_TOP 标识,而方式 2 无法为 Activity 指定singleInstance 模式。

另外还有点就是,在singleTask模式中,多次提到 Activity 所需的任务栈,那么这个任务栈是什么呢?这就要从一个参数说起:TaskAffinity,可以翻译为任务相关性,标识了一个Activity 所需要的任务栈的名字。我们可以为每个Activity 设置单独的TaskAffinity ,当然这个属性值不能设置成包名,否则就相当于没有指定。
TaskAffinity属性主要是和SingleTask模式或者allowTaskReparenting属性配对使用。那么allowTaskReparenting 的作用是什么呢?比如,从A程序 打开了B程序的 某个Activity ,如果这个Activity 的allowTaskReparenting 为 true的话,那么当应用B 被启动后将会显示B 的 某个Activity 界面。

1.2.2 Activity 的Flags

Activity 的 Flags 有很多,有的标记位可以设定 Activity 的启动模式,有的会影响 Activity 的运行状态等。常用的如下:

FLAG_ACTIVITY_NEW_TASK

其作用是为 Activity 指定 singleTask 启动模式,效果和在 XML 中指定该模式相同。

FLAG_ACTIVITY_SINGLE_TOP

其作用是为 Activity 指定 singleTop 启动模式,效果和在 XML 中指定该模式相同。

FLAG_ACTIVITY_CLEAR_TOP

具有此标记位的 Activity 启动时,在同一个任务栈中所有位于它上面的 Activity 都要出栈。

此模式一般需要和 FLAG_ACTIVITY_NEW_TASK 配合使用,若被启动Activity 的实例已存在,则系统会调用它的 onNewIntent

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

具有此标记的 Activity 不会出现在历史 Activity 的列表中(用于某些情况不希望用户通过历史列表回到 Activity 时)。它等同于在 XML 中指定 Activity 的属性 android:excludeFromRecents="true"。

1.3 IntentFilter 的匹配规则

启动Activity 分为两种,显式调用和隐式调用,显式调用需要明确地指定被启动对象的组件信息,包括类名和包名,而隐式调用需要明确的组件信息。这里我们主要介绍一下隐式调用。

隐式调用需要Intent能够匹配目标组件的IntentFilter 中所设置的过滤信息。

IntentFilter的过滤信息有action、category、data。为了匹配过滤列表,需同时匹配过滤列表中的action、category、data信息,否则匹配失败;一个Activity中可以有多个intent-filter,一个Intent只要能匹配任何一组intent-filter即可成功启动对应的Activity。

action的匹配规则

一个过滤规则中可以有多个action,Intent中的action能够和过滤规则中的任何一个action相同即可匹配成功。

如果Intent没有指定action,那么匹配失败。action区分大小写。

category的匹配规则

Intent中可以没有category;如果有,不管有几个,每个都要能和过滤规则中的任何一个category相同;

如果没有,依然可以匹配成功,原因是因为如果没有指定category,在调用startActivity或startActivityForResult时系统会默认加上“android.intent.category.DEFAULT”这个category。

同时为了我们的Activity能够支持隐式调用,就必须要在intent-filter中指定“android.intent.category.DEFAULT”这个category。

data的匹配规则

data的匹配规则和action类似,如果过滤规则定义了data,那么Intent必须定义可匹配的data。

data由两部分组成,mimeType和URI。mimeType是指媒体类型,比如image/jpeg、audio/mpeg4-generic和video/等,可以表示图片、文本、视频等不同的媒体格式,而URI中包含的数据就比较多了,下面是URI的结构*:

<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
//实际例子
content://com.example.project:200/folder/subfolder/etc
http://www.baidu.com:80/search/info

Scheme:URI的模式,比如http、file、content等,如果URI中没有指定scheme,那么整个URI的其他参数无效,也意味着URI是无效的。

Host:URI的主机名,比如www.baidu.com,如果host未指定,那么整个URI中的其他参数无效,也以为着URI无效。

Port:URI中的端口号,比如80,仅当URI中指定了scheme和host参数的时候port参数才是有意义的。

Path、pathPattern和pathPrefix:这三个参数都是表示路径信息;其中path表示完整的路径信息;pathPattern也表示完整路径信息,但是它里面可以包含通配符“”,“”表示0个或多个任意字符;pathPrefix表示路径的前缀信息。

Tips:

1. 当我们隐式启动一个Activity的时候,可以做一下判断,看是否能匹配到我们的隐式Intent,如果不做判断没找到对应的Activity系统就会抛出android.content.ActivityNotFoundException异常。

方法:采用PackageManager的resolveActivity方法或者Intent的resolveActivity方法,如果找不到匹配的Activity就会返回null,我们通过判断返回值就可以规避上述错误了。

2. 在intent-filter中声明了<category android:name="android.intent.category.DEFAULT"/>这个category的Activity,才可以接收隐式意图。

3. 有一类action和category的共同作用是标明这是一个入口Activity,并且会出现在系统的应用列表中,少一个都没有任何意义,也不会出现在系统的应用列表中。

<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

猜你喜欢

转载自blog.csdn.net/lhk147852369/article/details/84619610
今日推荐