Activity的生命周期,启动模式与Intent—filter匹配规则

第一章主要介绍Activity的生命周期和启动模式以及IntentFilter的匹配规则。

Activity的生命周期

Activity生命周期

  1. 点击应用图标启动,经历的生命周期为:

onCreate—>onStart—>onResume

  1. 按返回键

onPause—>onStop—>onDestory

  1. 按Home键

onPause—>onStop

  1. 按Home键退出后重新进入

onRestart—>onStart—>onResume

  1. 屏幕旋转时(不做任何配置)

onPause—>onStop—>onDestory—>onCreate—>onStart—>onResume

  1. 从一个ActivityA调到ActivityB(不销毁A):

onPause(A)—> onCreate(B)—>onStart(B)—>onResume(B)—>onStop(A) (如果finishA,那么后面执行onDestory(A))

Activity在异常情况下终止,系统会在onStop之前调用onSaveInstanceState来保存当前Activity的状态,Activity被重建后,系统会调用onRestoreInstanceState。在onCreate和onRestoreInstanceState中都可以恢复数据,建议在onRestoreInstanceState中进行数据恢复。一旦调用onRestoreInstanceState那么其中Bundle肯定是有值的,但是onCreate中就不一定了。

  1. 旋转屏幕时(配置android:configChanges=”orientation|keyboardHidden|screenSize”)

会发现不会重新调用各个生命周期。

Activity的启动模式

  1. standard:标准模式

默认的启动模式。

  1. singleTop:栈顶复用模式

如果当前的任务栈为ABC,如果C为SingleTop,再次启动C,那么仍然为ABC,如果C为standard,那么栈内会成为ABCC。

  1. singleTask:栈内复用模式
    如果当前任务栈内有ABCD四个Activity,要跳到B,由于B为singleTask,所以任务栈内会变为AB,将顶部的Activity清空。

  2. singleInstance:单实例模式。加强的singleTask,除了具有singleTask模式的所有特性外,此种模式的Activity只能单独地位于一个任务栈。

如何为Activity指定启动模式呢?

  1. 在AndroidMenifest为Activity指定启动模式;
  2. 通过在Intent中设置标志位来为Activity指定启动模式。

    Intent intent = new Intent();
    intent.setClass(MainActivity.this,NewActivity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

第二种的优先级要高于第一种,当两者存在时,以第二种方式为准。

如何为Activity指定任务栈呢?通过taskAffinity属性,该属性为一个字符串,中间包含包名分隔符。默认情况下,所有Activity所需的任务栈的名字为应用的包名。我们可以为每个Activity都单独指定TaskAffinity属性,这个属性值不能和包名相同,否则就相当于没有指定。TaskAffinity属性主要和singleTask启动模式或者allowReparenting属性配对使用,在其他情况下没有意义。任务栈分为前台任务栈和后台任务栈,后台任务栈中的Activity位于暂停状态。

当TaskAffinity和singleTask启动模式配对使用的时候,它是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中。

当TaskAffinity和allowTaskReparenting结合的时候。如果应用A启动了应用B的某个Activity后,如果这个Activity的allowTaskReparenting属性为true的话,那么当应用B被启动后,此Activity会直接从A的任务栈转移到应用B的任务栈。

举个例子:当网易云音乐分享到微信的时候,跳转到微信分享界面。这个时候按Home退到桌面。当我们点击微信的时候,会出现在分享界面。当微信被启动后,微信会创建自己的任务栈,这个时候系统发现微信分享界面所需要的任务栈已经被创建了,所以就把分享界面从网易云的任务栈转移过来了,重新找了个妈。

Tips

adb shell dumpsys activity:可以查看任务栈相关的信息

Activity的Flags

FLAG_ACTIVITY_NEW_TASK

为Activity指定“singleTask”启动模式,其效果和在XML中指定启动模式相同。

FLAG_ACTIVITY_SINGLE_TOP

为Activity指定“singleTop”启动模式,其效果和XML中指定启动模式相同。

FLAG_ACTIVITY_CLEAR_TOP

在它启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。
一般需要和FLAG_ACTIVITY_NEW_TASK配合使用,在这种情况下,被启动Activity的实例如果已经存在,那么系统就会盗用它的onNewIntent方法。如果被启动的Activity采用standard模式启动,那么它联通它之上的Activity都要出栈,系统会创建新的Activity并放入栈顶。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

具有这个标记的Activity不会出现在历史Activity的列表中,当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。等同于android:excludeFromRecents=”true”。

IntentFilter的匹配规则

只有一个Intent同时匹配action类别、category类别、data类别才算完全匹配,只有完全匹配才能启动目标Activity。

  1. action的匹配规则

系统预定义一些action,也可以定义自己的action。action的匹配规则是Intent中的action必须能够和过滤规则中的action匹配,这里的匹配是指action的字符串值完全一样,区分大小写。一个过滤规则中可以有多个action。

  1. category的匹配规则

category是一个字符串,系统预定义了一些category,我们也可以在应用中定义自己的category。Intent中如果含有category,那么所有的category都必须和过滤规则中的其中一个category相同。不设置category也可以匹配成功。那是因为startActivity或者startActivityForResult的时候会默认为Intent加上“android.intent.category.DEFAULT”。为了我们的activity能过接收隐式调用,就必须在intent-filter中指定”android.intent.category.DEFAULT”。

  1. data的匹配规则

如果定义规则中定义了data,那么Intent中必须也要定义可匹配的data。并且data数据能够完全匹配过滤规则中的某一个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,也可以表示图片、文本、视频等不同的媒体格式,而URI中包含的数据就比较多了。

URI的结构如下:

://:/[||]

例如:

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

path、pathPattern和pathPrefix:这三个参数表述路径信息,其中path表示完整的路径信息;pathPattern表示完整的路径信息,但是它里面可以包含通配符“”,如果想表示真实的字符串,“”要写成“\*”,”\”要写成”\\“;pathPrefix表示路径的前缀信息。

如果要为Intent指定完整的data,必须要调用setDataAndType方法,不能先调用setData再调用setType。

我们通过隐式方式启动一个Activity的时候,可以做一下判断,看是否有Activity能够匹配我们的隐式Intent,如果不做判断就有可能出现上述的错误。有两种方式:采用PackageManager的resolveActivity或者Intent的resolveActivity方法,如果找不到匹配的Activity就会返回null。PackageManager还提供了queryIntentActivities方法,这个方法和resolveActivity方法不同的是,它不是返回最佳匹配的Activity信息而是返回所有成功匹配的Activity。

查询匹配的Activity

第一个参数好理解,第二个参数需要注意,我们要使用MATCH_DEFAULT_ONLY这个标记位,这个标记位的含义是仅仅匹配那些在intent-filter中声明了这个category的Activity。使用这个标记的意义在于,只要上述两个方法不返回null,那么startActivity一定可以成功。如果不使用这个标记位,就可以把intent-filter中category不含DEFAULT的那些Activity匹配出来,从而导致startActivity可能失败。因为不含有DEFAULT这个category的Activity是无法接收隐式Intent的。

在action和category中,有一类action和category比较特殊:

这二者共同作用是用来标注这是一个入口Activity并且会出现在系统的应用列表中,少了任何一个都没有实际意义,也无法出现在系统的应用列表中。