Activity 生命周期和启动模式

一、Activity 的生命周期

1.1、正常情况下的生命周期

在这里插入图片描述

1、Activity 启动: onCreate —> onStart —> onResume

2、打开新的 Activity 或者切换到桌面: onPause —> onStop

  • 特殊情况:如果新 Activity 采用了透明主题,那么当前 Activity 不会回调 onStop。
  • 注意:打开新的 Activity 时,旧 Activity 的 onPause 先执行完后,然后新 Activity 才会启动,最后调用旧 Activity 的 onStop。

3、再次回到原 Activity 时: onRestart —> onStart —> onResume

4、当用户按下 back 键回退时: onPause —> onStop —> onDestory

5、被系统回收后再次打开

  • 生命周期方法和 Activity 启动时一样,注意是生命周期方法一样,不代表所有过程都一样。

6、成对的回调

  • 从整个生命周期来说,onCreateonDestroy 配对。
    分别标识着 Activity 的创建和销毁。
  • 从 Activity 是否可见来说,onStartonStop 配对。
    随着用户的操作或者设备屏幕的点亮和熄灭,这两个方法可能被调用多次。
  • 从 Activity 是否在前台来说,onResumeonPause 配对。
    随着用户的操作或者设备屏幕的点亮和熄灭,这两个方法可能被调用多次。

1.2、异常情况下的生命周期

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

在这里插入图片描述

  • onSaveInstanceStateonStop 前调用,和 onPause 没有既定的时序关系。

  • onSaveInstanceStateonRestoreInstanceState 只会在 Activity 被异常终止的情况下调用。

  • Activity 被重新创建后,系统会调用 onRestoreInstanceState,把销毁时在 onSaveInstanceState 中保存的 Bundle 对象作为参数同时传递给 onRestoreInstanceStateonCreate 方法。因此,可以通过 onRestoreInstanceState 和 onCreate 方法判断 Activity 是否被重建了。

    二者区别:
    onRestoreInstanceState 一旦被调用,其参数 Bundle savedInstanceState 一定是有值的,不用额外判断是否为空;onCreate 在正常启动的情况下,其参数 Bundle saveInstanceState 为 null,因此还需要额外判断。

  • onRestoreInstanceState 调用时机在 onStart 之后。

  • 阻止系统重新创建 Activity,例如阻止屏幕旋转时重建 Activity ,配置属性 android:configChanges=“orientation” 就可以了,一次性可以配置多个,用 “|” 连接。 configChanges 属性可以配置的项目

  • 当 Activity 异常情况下被重建时,系统默认保存当前 Activity 的视图结构,并在 Activity 重启后恢复这些数据,比如文本框中用户输入的数据、ListView 滚动位置等。和 Activity 一样,每个 View 都有 onSaveInstanceState 和 onSaveInstanceState 方法,通过查看源码,就能知道系统能够自动为每个 View 恢复哪些数据。

  • 关于保存和恢复 View 层次结构,系统的工作流程:首先 Activity 被意外终止,Activity 会调用 onSaveInstanceState 保存数据,然后 Activity 会委托 Window 保存数据,接着 Window 再委托上面的顶级容器保存数据。顶层容器是一个 ViewGroup,一般来说它很可能是 DecorView。最后顶层容器再去一一通知它的子元素保存数据,这样就完成了保存数据的过程。数据恢复过程也类似。

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

  • 前台 Activity——正在和用户交互的 Activity,优先级最高。
  • 可见但非前台 Activity——比如 Activity 弹出了一个对话框,导致 Activity 可见但是位于后台无法和用户直接交互。
  • 后台 Activity——已经被暂停的 Activity,比如执行了 onStop,优先级最低。
  • 当系统内存不足时,系统会按照优先级杀死目标 Activity 所在的进程。后续也可以通过 onSaveInstanceStateonRestoreInstanceState恢复数据。
  • 没有四大组件在其中执行的进程将很快被杀死。将后台工作放入 Service 中从而保证进程有一定的优先级。

二、Activity 的启动模式

2.1、standard:标准模式,这也是系统的默认模式

  • 每次启动一个 Activity 都会重新创建一个新的实例,不管这个实例是否已经存在。
  • standard 模式的 Activity 默认会进入启动它的 Activity 所属的任务栈中。

2.2、singleTop:栈顶复用模式

  • 如果新 Activity 位于任务栈的栈顶,那么此 Activity 不会被重新创建,不再调用 onCreate、onStart,会回调 onNewIntent(Intent intent) 方法。

2.3、singleTask:栈内复用模式

  • 只要 Activity 在一个栈中存在,那么多次启动此 Activity 都不会重新创建实例,不再调用 onCreate、onStart,会回调 onNewIntent(Intent intent) 方法。
  • 如果栈内不存在,需要重新创建时,系统会寻找是否存在新 Activity 需要的任务栈,如果不存在,就重新创建一个任务栈,然后创建新 Activity 的实例后放到栈中。
  • 默认具有 clearTop 的效果,所有在此 Activity 上面的 Activity 全部出栈。

2.4、singleInstance:单实例模式

  • 这是一种加强的 singleTask 模式,具有 singleTask 模式的所有特性。
  • 这种模式的 Activity 只能单独存在于一个任务栈中。
  • 后续的请求均不会创建新的 Activity ,除非这个独特的任务栈被销毁了。

2.5、给 Activity 指定启动模式

  1. 通过 AndroidMenifest
        <activity
            android:name=".SecondActivity"
            android:configChanges="screenLayout"
            android:label="@string/app_name"
            android:launchMode="singleTask" />
  1. 通过 Intent 标志位
        Intent intent = new Intent();
        intent.setClass(MainActivity.this, SecondActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
  • 两者区别
    1、优先级上,第二种高于第一种,当两种同时存在,以第二种为准。
    2、限定范围有所不同,比如,第一种方式无法直接为 Activity 设定 FLAG_ACTIVITY_CLEAR_TOP 标识,而第二种方式无法为 Activity 指定 singleInstance 模式。

  • 这里说几个常用标记位
    1、Intent.FLAG_ACTIVITY_NEW_TASK:为 Activity 指定 singleTask 启动模式。
    2、Intent.FLAG_ACTIVITY_SINGLE_TOP:为 Activity 指定 singleTop 启动模式。
    3、Intent.FLAG_ACTIVITY_CLEAR_TOP:当启动具有此标记位的 Activity时,在同一任务栈中的所有位于它之上的 Activity 都要出栈。
    4、Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有此标记的 Activity 不会出现在历史 Activity 的列表中,相当于 XML 中 Activity 的 android:excludeFromRecents=“true” 属性。

三、Activity 的任务栈

  • 默认情况下,所有 Activity 所需的任务栈的名字都为应用的包名。
  • 单独指定任务栈,需要设置 Activity 的属性 android:taskAffinity="",不能和包名相同,否则相当于没有指定。taskAffinity 属性主要和 singleTask 启动模式或者 allowTaskReparenting 属性配对使用,否则没有意义。
    1、taskAffinity 和 singleTask 启动模式配对的时候
    指定了具有 singleTask 启动模式的 Activity 的任务栈的名字,待启动的 Activity 会运行在 taskAffinity 指定的任务栈中。
    2、taskAffinity 和 allowTaskReparenting 配对的时候
    当一个应用 A 启动了应用 B 的某个 Activity 后,如果这个 Activity 的 allowTaskReparenting 属性为 true,那么当在桌面再次启动应用 B 后,此 Activity 会直接从应用 A 的任务栈转移到应用 B 的任务栈中。
  • 任务栈分为前台任务栈和后台任务栈,后台任务栈中的 Activity 处于暂停状态,用户可以通过切换将后台任务栈再次调到前台。
  • 假设目前有 2 个任务栈,前台任务栈为 AB,后台任务栈为 CD,那么启动 D 时和启动 C 时的情况如下。

在这里插入图片描述

在这里插入图片描述
小栗子

  • 将 SecondActivity 和 ThirdActivity 都设成 singleTask 并指定 taskAffinity 属性为 “com.example.task1",注意这个 taskAffinity 属性的值为字符串,且中间必须包含有包名分隔符 “.”.
  • 在 MainActivity 中启动 SecondActivity,在 SecondActivity 中启动 ThirdActivity,在 ThirdActivity 中启动 MainActivity,最后再在 MainActivity 中启动 SecondActivity,最后按两次 back 键,会发现回到了桌面。
  • MainActivity 的启动模式为默认模式 standard。
    在这里插入图片描述

四、IntentFilter 的匹配规则

3.1、显式调用

        Intent intent = new Intent(MainActivity.this, SecondActivity.class);
        startActivity(intent);

3.2、隐式调用

  • intent-filter
    1、一个 Activity 中可以有多组 intent-filter,一个 Intent 只要能匹配任意一组 intent-filter 即可成功启动对应的 Activity。Intent 必须同时匹配某一组 intent-filter 中的 action、category、data 才算完全匹配,只有完全匹配才能启动 Activity。
    2、一个 intent-filter 中的 action、category、data 都可以有多个。

  • action 匹配规则
    Intent 中必须指定 action,并且要和 intent-filter 中的任意一个 action 相同(区分大小写),才能成功匹配。

  • category 匹配规则**
    1、如果 Intent 中有 category,那么必须匹配 intent-filter 中的 category 。
    2、Intent 不设置 category 也可以匹配,原因是系统在调用 startActivity 或者 startActivityForResult 时默认给 Intent 加上了 android.intent.category.DEFAULT,所以在隐式调用时 intent-filter 中必须加 <category android:name=“android.intent.category.DEFAULT” />。

  • data 匹配规则(关于 data 还存在疑问,仅供参考)
    1、如果 intent-filter 中定义了 data,那么 Intent 中必须指定 data,并且要和 Intent-filter 中的 data 相对应,才能成功匹配。
    2、data 由两部分组成,mimeType 和 URL。
    3、mimeType 指媒体类型,比如 image/jpeg、audio/mpeg4-generic 和 video/* 等,可以表示图片、文本、视频等不同的媒体格式。
    4、而 URI 中包含的数据就比较多了,下面是 URI 的结构。

<scheme>://<host>:<post>/[<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、pathPrefix 和 pathPattern:这三个参数表述路径信息。
    path 表示完整的路径信息。
    pathPrefix 表示路径的前缀信息。
    pathPattern 也表示完整的路径信息,但是它里面可以包含通配符 “*”,“*” 表示 0 个或多个任意字符,需要注意的是,由于正则表达式的规范,如果想要表示真实的字符串,那么 “*” 要写成 “\\*”,"\"要写成
    “\\\\”。
  1. 拨打电话,
intent.setAction("android.intent.action.CALL");
intent.setData(Uri.parse("tel://13600000000"));
  1. 打开浏览器
intent.setAction("android.intent.action.VIEW");
intent.setData(Uri.parse("https://www.baidu.com/"));
  1. 播放音频
intent.setAction("android.intent.action.VIEW");
intent.setDataAndType(Uri.parse("file:///mnt/sdcard/Adele - Rolling in the Deep.mp3"), "audio/*");
  1. 播放视频
intent.setAction("android.intent.action.VIEW");
intent.setDataAndType(Uri.parse("file:///mnt/sdcard/1.mp4"), "video/*");
  1. 显示图片
intent.setAction("android.intent.action.VIEW");
intent.setDataAndType(Uri.parse("file:///mnt/sdcard/9.jpg"), "image/*");
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值