《Android开发艺术探索》读书笔记——Activity的生命周期和启动模式


一、Activity的生命周期全面分析

典型情况下的生命周期是指在有用户参与的情况下,Activity所经过的生命周期的改变;异常情况下的生命周期是指Activity被系统回收或者由于当前设备的Configuration发生改变(例如手机横竖屏切换)从而导致Activity被销毁重建。


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

Activity生命周期图

正常情况下,Activity的常用生命周期只有上面7个。

(1)一个Activity第一次启动时,回调如下:onCreate -> onStart -> onResume。

(2)当用户打开新的Activity或者使用Home键切换到桌面时,回调如下:onPause -> onStop。

    如果新Activity采用了透明主题,那么当前Activity不会回掉onStop。

(3)当用户再次回到原Activity时,回调如下:onRestart -> onStart -> onResume。

(4)当用户按back键回退时,回调如下:onPause -> onStop -> onDestroy。

(5)从整个生命周期来说,onCreate和onDestroy是配对的,分别表示着Activity的创建和销毁,并且只可能调用一次。

onStart和onStop是配对的,是从Activity是否可见这个角度来回调。onResume和onPause是配对的,是从Activity是否位于前台这个

角度来回调。

(6)当新启动一个Activity时,旧Activity的onPause先调用,然后新Activity才启动,所以不能在onPause中做重量级的操作

    从Activity A打开Activity B,生命周期回掉如下:onPause(A) -> onCreate(B) -> onStart(B) -> onResume(B) -> onStop(A)。


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

(1)异常情况:系统配置发生改变或者系统内存不足时,Activity被杀死。

(2)Activity被异常终止时,系统会调用onSaveInstanceState来保存当前Activity的状态。这个方法的调用时机是在onStop之前,

它和onPause没有既定的时序关系,它即可能在onPause之前调用,也可能在onPause之后调用。当Activity被重新创建时,

系统会调用onRestoreInstanceState来恢复之前保存的数据,调用时机是在onStart之后。

系统只会在Activity即将被销毁并且有机会重新显示的情况下才会去调用onSaveInstanceState方法。

当Activity在异常情况下需要重新创建时,系统会默认为我们保存当前Activity的视图结构,并且在Activity重启后为我们恢复这些数

据,比如文本框中用户输入的数据、listview滚动的位置等,这些view相关的状态系统都会默认为我们恢复。具体针对某一个view系

统能为我们恢复哪些数据可以查看view的源码中的onSaveInstanceState和onRestoreInstanceState方法。

(3)Activity的优先级从高到低:前台Activity,可见但非前台Activity,后台Activity。

当系统内存不足时,系统会杀死优先级低的Activity。

(4)配置android:configChanges="xxx"属性,常用的主要有下面三个:
local:设备的本地位置发生了变化,一般指切换了系统语言;
keyboardHidden:键盘的可访问性发生了变化,比如用户调出了键盘;
orientation:屏幕方向发生了变化,比如旋转了手机屏幕。
配置了android:configChanges="xxx"属性之后,Activity就不会在对应变化发生时重新创建,而是调用Activity的
onConfigurationChanged方法


二、Activity的启动模式


Activity的LaunchMode

(1)任务栈是一种“先进后出”的栈结构,当栈中无任何Activity的时候,系统会回收这个任务栈。

(2)standard:标准模式,系统的默认模式。每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否已经存在。

用ApplicationContext去启动standard模式的Activity的时候会报错,因为standard模式Activity默认会进入启动它的Activity所属的

任务栈中,但是由于非Activity类型的Context(如ApplicationContext)并没有所谓的任务栈。解决的方法是为待启动Activity指定

FLAG_ACTIVITY_NEW_TASK标记位。

(3)singleTop:栈顶复用模式。在这种模式下,如果新的Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,

同时它的onNewIntent方法会被回调,但这个Activity的onCreate、onStart方法不会被系统调用。

如果新Activity的实例已存在但不是位于栈顶,那么新Activity仍然会重新重建。

(4)singleTask:栈内复用模式。在这种模式下,只要Activity在栈中存在,那么多次启动此Activity都不会重新创建实例,系统也会

回调其onNewIntent方法。singleTask默认具有clearTop的效果,会导致栈内所有在D上面的Activity全部出栈。

(5)singleInstance:单实例模式。加强的singleTask模式,具有此种模式的Activity只能单独地位于一个任务栈中。

启动singleInstance系统会为它创建一个新的任务栈,由于栈内复用的特性,后续的请求均不会创建新的Activity,除非这个独特的

任务栈被系统销毁。

(6)有两种方法可以给Activity自动启动模式:

第一种是通过AndroidMenifest:

<activity
            android:name="com.ryg.chapter_1.SecondActivity"
            android:configChanges="screenLayout"
            android:label="@string/app_name"
            android:launchMode="singleTask" />

另一种是通过在Intent中设置标志位来为Activity指定启动模式:

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

Activity的Flags

(1)FLAG_ACTIVITY_NEW_TASK:这个标记位的作用是为Activity指定“singleTask”启动模式,其效果和在XML中指定该启动模式相

同。

(2)FLAG_ACTIVITY_SINGLE_TOP:这个标记位的作用是为Activity指定“singleTop”启动模式,其效果和在XML中指定该启动模式

相同。

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

(4)FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有这个标记的Activity不会出现在历史Activity的列表中,当某些情况下我们

不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。等同于在XML中指定Activity的属性android:excludeFromRecents="true"。


三、IntentFilter的匹配规则

启动Activity分为两种,显式调用和隐式调用。显示调用需要明确地指定被启动对象的组件信息,包括包名和类名。隐式调用需要

Intent能够匹配目标组件的IntentFilter中所设置的过滤信息。只有一个Intent同时匹配action类别、category类别、data类别才算完全

匹配,只有完全匹配才能成功启动目标Activity。


action的匹配规则

(1)action的匹配规则是Intent中的action必须能够和过滤规则中的action的字符串值完全一样。

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

(3)action区分大小写,大小写不同字符串相同的action会匹配失败。


category的匹配规则

(1)category的匹配规则是如果Intent中出现了category,不管有几个category,每个都要能够和过滤规则中的任何一个catagory相同。

(2)Intent中没有category也可以匹配成功,因为系统在用startActivity或者startActivityForResult的时候会默认为Intent加上

“android.intent.category.DEFAULT”这个category。

(3)为了我们的activity能够接收隐式调用,就必须在intent-filter中指定“android.intent.category.DEFAULT”这个category。


data的匹配规则

(1)data的结构比较复杂,语法大致如下:

<data android:scheme="string"
      android:host="string"
      android:port="string"
      android:path="string"
      android:pathPattern="string"
      android:pathPrefix="string"
      android:mimeType="string" />
data由两部分组成,mimeType和URI。mimeType指媒体类型,可以表示图片、文本、视频等不同的媒体格式,比如image/jpeg、

video/*等。URI的结构也比较复杂,语法大致如下:

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

content://com.example.project:200/folder/subfolder/etc
scheme、host、port分别表示URI的模式、主机名和端口号,其中如果scheme或者host未指定那么URI就无效。

path、pathPattern、pathPrefix都是表示路径信息,其中path表示完整的路径信息,pathPrefix表示路径的前缀信息;

pathPattern表示完整的路径,但是它里面包含了通配符(*)。

(2)data匹配规则要求Intent中必须含有data数据,并且data数据能够完全匹配过滤规则中的某一个data。

(3)URI的schema有默认值:如果过滤规则中mimeType属性指定为“image/*”、“video/*”等类型,如果过滤规则中没有指定URI,

URI的schema的默认值为content或者file。

(4)如果要为Intent指定完整的data,必须要调用setDataAndType方法,不能先调用setData在调用setType,因为这两个方法彼此会

清除对方的值。

intent.setDataAndType(Uri.parse("file://abc"), "image/png");
(5)判断是否有Activity能够匹配我们的隐式Intent:

  1. 采用PackageManager的resolveActivity方法或者Intent的resolveActivity方法,如果它们找不到匹配的Activity就会返回null,我们通过判断返回值就可以规避无法找到Activity错误。
  2. PackageManager还提供queryIntentActivities方法,它返回所有成功匹配的Activity信息。

<span style="font-size:14px;">public abstract ResolveInfo resolveActivity(Intent intent, int flags);
public abstract List<ResolveInfo> queryIntentActivities(Intent intent, int flags);
</span>

第二个参数要使用MATCH_DEFAULT_ONLY标记位,这个标记位的含义是仅仅匹配那些在intent-filter中声明了

<category android:name="android.intent.category.DEFAULT"/>这个category的Activity。这个标记位的意义在于,只要上面两个方法

不返回null,那么startActivity一定可以成功。如果不用这个标记位,就可以把intent-filter中category不含DEFAULT的那些Activity给匹

配出来,从而导致startActivity可能失败。因为不含有DEFAULT这个category的Activity是无法接收隐式Intent的

(6)有一类action和category比较重要:

<intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
这两者共同作用是用来标明这是一个入口Activity,并且会出现在系统的应用列表中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值