Activity

再谈Activity

目录

  1. 生命周期
    1. 正常生命周期
    2. 非正常生命周期
  2. Activity的启动模式
    1. Activity的LaunchMode
    2. Activity的Flags
  3. IntentFilter的匹配规则
  4. 附加

生命周期

正常的生命周期

onCreate()->onStart()->onResume()->onPause()->onStop()->onDestroy()

  • onCreate():生命周期的第一个方法,Activity在创建时调用
  • onRestart():表示Activity正在重新启动,当Activity从不可见状态变为可见状态时调用。一般在调用了onStop但并没有销毁Activity,再次展示时会调用。
  • onStart():表示Activity正在被启动,Activity创建完成后调用,此时Activity可见但不可与用户交互。可以理解成Activity已经显示出来但是并没有出现在屏幕上。
  • onResume:表示Activity已经可见,并出现在屏幕上可与用户交互,注意与onStart的对比,onStart和onResume都已经可见,但是onStart还在后台,onResume的时候才渲染到前台
  • onPause():表示Activity正在停止,之后一般会立刻调用onStop方法,可以在此方法进行数据保存和停止动画等操作,但是不能执行耗时操作,否则会影响第二个Activity的显示速度,因为新Activity的onResume方法会在第一个Activity的onPause执行完后才会执行。
  • onStop():表示Activity即将停止,此时Activity处于后台,可以适当的做一些耗时操作
  • onDestroy():表示Activity即将被销毁,是生命周期中的最后一个方法,可以做一些资源回收和释放

Activity的生命周期代表的是Activity所在的状态,调用是AMS通过调用ActivityThread,ActivityThread调用对应的Activity实现的,也就代表着自行手动调用生命周期方法无法完成Activity生命周期状态的切换。

image

根据上图,附加具体说明,分为以下几种情况:

  1. 一个Activity第一次启动时,回调onCreate->onStart->onResume
  2. 当打开新Activity时,onPause(old)->onCreate(new)->onStart(new)->onResume(new)->onStop(old)
  3. 当从新Activity返回旧Activity时,onPause(new)->onRestart(old)->onStart(old)->onResume(old)->onStop(new)->onDestroy(new)
  4. 当按home键时,onPause->onStop,重新打开时,onRestart->onStart->onResume
  5. 当按back键时,onPause->onStop->onDestroy
  6. 被系统回收后,再次打开,生命周期和1一样,具体过程会增加数据恢复的过程
  7. 从整体来说,除了onRestart外其他生命周期都是成对出现的,onCreate和onDestroy分别标识着创建和销毁,onStart和onStop分别标识着Activity是否可见,onResume和onPause分别标识着Activity是否可交互

非正常的生命周期

上面我们讲了正常的生命周期,那么非正常的生命周期是什么:当系统内存不足,或者资源相关的配置发生变化时Activity会被系统杀死。

情况1:资源相关的配置发生变化导致Activity销毁重建

默认情况下,如果我们的Activity不做特殊配置,那么当系统配置发生改变后,Activity就会被销毁并重建。生命周期:onSaveInstanceState->onStop->onDestroy->onCreate->onStart->onRestoreInstanceState->onResume

上面的生命周期可以看到没有onPause,这是因为onPause和onSaveInstanceState的执行顺序没有既定的时序关系。onSaveInstanceState在onStop之前执行,onRestoreInstanceState在onResume之前onStart之后执行。

保存和恢复View层次结构采用的典型委托思想,系统的工作流程是:首先Activity被意外停止时,Activity会调用onSaveInstanceState去保存数据,然后Activity委托Window去保存数据,接着Window委托它上面的顶层容器去保存数据。顶层容器是一个ViewGroup,一般是DecorView。最后顶层容器再去一一通知它的子元素来保存数据。

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

这种情况的数据保存和恢复过程跟情况1是一样的。我们描述一下Activity的优先级情况。Activity的优先级从高到低可以分成如下三种:

  1. 前台Activity—正在和用户交互的Activity,优先级最高
  2. 可见但不可交互的Activity—比如Activity中弹出一个对话框,导致Activity可见但是处于后台无法与用户进行交互
  3. 后台Activity—已经被暂停的Activity,比如执行了onStop,优先级最低

上面的非正常周期说的没有任何其他配置的时候的生命周期,我们这里的其他配置指的是Activity的android:configChanges属性,比如横竖屏切换,正常的横竖屏切换会执行情况1的生命周期,但是如果给Activity的configChanges属性设置了orientation那么横竖屏切换就不会执行onPause->onStop->onDestroy->onCreate->onStart->onResume而只会执行onConfigurationChanged方法。

configChanges的项目和含义

项目含义
mccSIM卡唯一标识IMSI(国际移动用户识别码)中的国家代码,由三位数字组成,中国为460
mncSIM卡唯一标识IMSI(国际移动用户识别码)中的运营商代码,由两位数字组成,中国移动TD系统为00,中国联通为01,中国电信为03
locale设备的本地位置发生了改变,一般指切换了系统语言
touchscreen触摸屏发生了改变,正常情况下无法发生
keyboard键盘类型发生了改变,比如用户使用了外接键盘
keyboardHidden键盘的可访问性发生了改变,比如用户调出了键盘
navigation系统导航方式发生了改变,比如采用了轨迹球导航
screenLayout屏幕布局发生了改变,比如用户激活了另外一个显示设备
fontScale系统字体缩放比例发生了改变,比如用户选择了一个新字号
uiMode用户界面模式发生了改变,比如是否开启了夜间模式(API 8新添加)
orientation屏幕方向发生了改变,比如旋转了手机屏幕
screenSize当屏幕的尺寸信息发生了改变,当旋转设备屏幕时,屏幕尺寸会发生变化,这个选项比较特殊,它和编译选项有关,当编译选项中的minSdkVersion和targetSDKVersion均低于13时,此选项不会导致Activity重启,否则会导致Activity重启(API 13新添加)
smallestScreenSize设备的物理屏幕尺寸发生改变,这个项目和屏幕的方向没有关系,仅仅表示在实际的物理屏幕的尺寸改变的时候发生,比如用户切换到了外部的显示设备,这个选项和screenSize一样,当编译选项中的minSdkVersion和targetSDKVersion均低于13时,此选项不会导致Activity重启,否则会导致Activity重启(API 13新添加)
layoutDirection当布局方向发生改变,正常情况下无需修改布局的layoutDirection属性(API 17新添加)

Activity的启动模式

Activity的LaunchMode

  1. Activity为什么需要启动模式

    默认情况下,当我们多次启动同一个Activity时,系统会创建多个实例并把它们一一压入任务栈中,当我们单机back键,会发现这些Activity会一一回退。任务栈是一种“后进先出”的栈结构,可以这样理解,每按一下back键就会有一个Activity出栈,直到栈空为止,当栈中无任何Activity时,系统就会回收这个任务栈。如果我们多次启动同一个Activity,系统重复创建多个实例,退出的时候还要逐一退出,这样不是很傻。所以Android为我们提供了启动模式来修改系统的默认行为。

  2. 四种启动模式

    1. standard:标准模式,这也是系统的默认模式。每次启动一个Activity都会重新创建一个新的实例。
    2. singleTop:栈顶复用,如果要启动的Activity已经在栈顶,则不会重新创建Activity,同时该Activity的onNewIntent方法会被调用。如果要启动的Activity不在栈顶,则会重新创建该Activity的实例。
    3. singleTask:栈内复用,如果要启动Activity已经在它想要归属的栈中,那么不会创建新的实例,将栈中位于该Activity上的所有Activity出栈,同时该Activity的onNewIntent会被调用。如果要启动的Activity在它想要归属的栈中,并且该栈存在,则会重新创建该Activity的实例。如果要启动的Activity想要归属的栈不存在,则首先创建一个新栈,然后创建该Activity实例并压入到新栈中。
    4. singleInstance:和singleTask类似,不同的是启动Activity时,首先要创建一个栈,然后创建该Activity的实例并压入栈中,新栈中只会存在这一个Activity实例。
  3. 多任务栈

    1. 任务栈是什么,在AMS中保存的所有TaskRecord统称任务栈,所有启动的Activity都保存在某一个任务栈中。

    2. 如何创建任务栈,我们无法在AMS外部自己创建任务栈,只能给Activity配置taskAffinity属性,AMS检测taskAffinity自动创建任务栈。没有配置taskAffinity的话,除singleInstance模式之外的Activity将自动创建名称为应用包名的任务栈。

    3. Activity如何入栈,AMS中可以有同名任务栈

      1. standard与singleTop的Activity将压入启动该Activity的栈
      2. singleTask的Activity如果没有指定taskAffinity将压入应用主栈,如果指定了taskAffinity将压入指定名称的栈中
  4. singleInstance的Activity只能单独存在与一个栈中,如果没有指定taskAffinity创建一个名称与包名一致的新栈并入栈,如果指定了taskAffinity将创建一个对应名称的新栈并入栈

    1. allowTaskReparenting

      allowTaskReparenting为true重新分配,false则不重新分配,默认为false。重新分配task标记。例:现在有2个应用A和B,A启动了B的一个ActivityC,然后按Home回到桌面,然后点击B的桌面图标,这个时候并不是启动B的主Activity,而是显示已经被A启动的ActivityC,或者说C从A的栈转移到B的栈中。

      可以这样理解,如果C没有新建栈的能力,C是B的,但是A启动了C,此时没有B那它就只能入A的栈,B启动之后,系统发现C想要归属的栈被创建,直接将C从A转移到B

Activity的Flags

Activity的启动模式有两种方式可以指定

  1. 通过AndroidManifest给Activity指定启动模式
  2. 通过在Intent中设置标志位给Activity指定启动模式

常用的Flags

  1. FLAG_ACTIVITY_NEW_TASK

    指定“singleTask”启动模式,效果跟xml指定相同

  2. FLAG_ACTIVITY_SINGLE_TOP

    指定“singleTop”启动模式,效果跟xml指定相同

  3. FLAG_ACTIVITY_CLEAR_TOP

    具有此标记位的Activity,当它启动时,在同一个任务栈中所有位于它上面的Activity都有出栈。这个标记位一般会和singleTask启动模式一起出现,在这种情况下,被启动的Activity的实例如果已经存在,那么系统就会调用它的onNewIntent。如果被启动的Activity采用standard模式启动,那么它连同它之上的Activity都要出栈,系统会创建新的Activity实例并放入栈顶。singleTask启动模式默认具有此标记位的效果

  4. FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

    具有这个标记位的Activity不会出现在历史Activity的列表中,等同于xml中指定Activity的属性android:excludeFromRecents=“true”

IntentFilter的匹配规则

Activity的启动分为两种:

  1. 显示调用

    指定被启动对象的组件信息,包括包名和类名

  2. 隐式调用

    不需要明确的指定组件信息,使用IntentFilter中的过滤信息action、category、data进行匹配

原则上,一个intent不应该既有显示调用又有隐式调用,如果二者并存,以显示调用为准

  1. action的匹配规则

    action是一个字符串,系统预定义了一些action,同时我们也可以在应用中定义自己的action。action的匹配规则是Intent中的action必须能够和过滤规则中的action匹配,这里的匹配是指action的字符串值完全一样。一个过滤规则中可以有多个action,那么只要Intent中的action能够和过滤规则中的任何一个action相同即可匹配成功。

    action的匹配要求Intent中的action存在且必须和过滤规则中的某一个action相同,区分大小写。

  2. category的匹配规则

    category是一个字符串,系统预定义了一些category,同时我们也可以在应用中定义自己的category。category的匹配规则和action不同,不是必须的,但是如果Intent中指定了category,那么指定的所有category必须都跟过滤规则中的某一个的字符串值一样。为什么可以不知道category,因为系统会自动为Intent加上”android.intent.category.DEFAULT“这个category。我们在定义可隐式调用的Activity时也就必须在intent-filter中指定”android.intent.category.DEFAULT“这个category。

    category的匹配要求Intent中可以不指定category,但是如果指定,则所有category都必须跟过滤规则中的某一个category相同。

  3. data的匹配规则

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

    data的语法

     <data
    		android:host="string"
            android:mimeType="string"
            android:path="string"
            android:pathPattern="string"
            android:pathPrefix="string"
            android:port="string"
            android:scheme="string" />
    

    data由两部分组成,mimeType和URI。mimeType指媒体类型,比如image/jpeg、audio/mpeg4-generic和video/*等,可以表示图片、文本、视频等不同的媒体格式。

    URI的结构

    <scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
    

    例:

    content://com.example.project:200/folder/subfolder/etc
    http://www.baidu.com:80/search/info
    

    各数据含义:

    1. Scheme:URI的模式,比如http、file、content等,如果URI中没有指定scheme,那么整个URI的其他参数无效,这也意味着URI是无效的
    2. Host:URI的主机名,比如www.baidu.com,如果host未指定,那么整个URI中的其他参数无效,这也意味着URI是无效的
    3. Port:URI的端口号,比如80,仅当URI中指定了scheme和host参数的时候port参数才是有意义的
    4. path、pathPattern和pathPrefix:这三个参数表述路径信息,其中path表示完整的路径信息;pathPattern也表示完整的路径信息,但是它里面可以包含通配符“*”,“**”表示0个或多个任意字符,需要注意的是,由于正则表达式的规范,如果想表示真实的字符串,那么“*“要写成”\\*","\“要写成“\\\\”;pathPrefix表示路径的前缀信息。

    介绍完data的数据格式后,我们说一下匹配规则。data的匹配规则和action类似,它也要求Intent中必须含有data数据,并且data数据能够完全匹配过滤规则中的某一个data。这里的完全匹配是指过滤规则中出现的data部分也出现在了Intent中的data中。

    data的URI有默认值,默认值为content和file。所以在过滤规则中没有指定URI的时候,Intent中的URI部分的scheme必须为content或者file才能匹配。

    如果过滤规则中有data则Intent中必须有data能与过滤规则中的任何一个data一致

附加

  1. 隐式调用Activity

    当我们通过隐式调用启动一个Activity时,可以做一下判断,看是否有Activity能够匹配我们的隐式Intent。判断方式有两种:

    1. 采用PackageManager的resolveActivity方法
    2. Intent的resolveActivity方法

    如果找不到匹配的Activity就会返回null。

    PackageManager还提供了queryintentActivities方法,这个方法和resolveActivity方法不同的是,它返回的不是最佳匹配的Activity信息而是返回所有成功匹配的Activity信息

  2. Activity各启动模式应用场景

    1. standard

      系统默认的启动模式,我们开发一个应用很多Activity都会使用此模式

    2. singleTop

      栈顶复用模式,Activity A启动Activity A可以使用此模式,降低性能消耗。例如:某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面就不合理了

    3. singleTask

      栈内复用,整个APP里只有一个的Activity可以使用此模式,例如:首页,不管是从哪进入首页都需要进入的是同一个首页,所以可以采用此模式

    4. singleInstance

      适用于需要于程序分离的界面,例如:闹钟提醒界面,需要将闹钟提醒界面与闹钟设置界面分离。singleInstance不要用于中间页,如果用于中间页,跳转会有问题,比如,A->B(singleInstance)->C,按返回键,会从C直接退到A,再按返回键会将A退出,将B展示出来

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值