第一章主要介绍Activity的生命周期和启动模式以及IntentFilter的匹配规则。
onCreate—>onStart—>onResume
onPause—>onStop—>onDestory
onPause—>onStop
onRestart—>onStart—>onResume
onPause—>onStop—>onDestory—>onCreate—>onStart—>onResume
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中就不一定了。
会发现不会重新调用各个生命周期。
默认的启动模式。
如果当前的任务栈为ABC,如果C为SingleTop,再次启动C,那么仍然为ABC,如果C为standard,那么栈内会成为ABCC。
singleTask:栈内复用模式
如果当前任务栈内有ABCD四个Activity,要跳到B,由于B为singleTask,所以任务栈内会变为AB,将顶部的Activity清空。
singleInstance:单实例模式。加强的singleTask,除了具有singleTask模式的所有特性外,此种模式的Activity只能单独地位于一个任务栈。
如何为Activity指定启动模式呢?
通过在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退到桌面。当我们点击微信的时候,会出现在分享界面。当微信被启动后,微信会创建自己的任务栈,这个时候系统发现微信分享界面所需要的任务栈已经被创建了,所以就把分享界面从网易云的任务栈转移过来了,重新找了个妈。
adb shell dumpsys activity:可以查看任务栈相关的信息
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”。
只有一个Intent同时匹配action类别、category类别、data类别才算完全匹配,只有完全匹配才能启动目标Activity。
系统预定义一些action,也可以定义自己的action。action的匹配规则是Intent中的action必须能够和过滤规则中的action匹配,这里的匹配是指action的字符串值完全一样,区分大小写。一个过滤规则中可以有多个action。
category是一个字符串,系统预定义了一些category,我们也可以在应用中定义自己的category。Intent中如果含有category,那么所有的category都必须和过滤规则中的其中一个category相同。不设置category也可以匹配成功。那是因为startActivity或者startActivityForResult的时候会默认为Intent加上“android.intent.category.DEFAULT”。为了我们的activity能过接收隐式调用,就必须在intent-filter中指定”android.intent.category.DEFAULT”。
如果定义规则中定义了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。
第一个参数好理解,第二个参数需要注意,我们要使用MATCH_DEFAULT_ONLY这个标记位,这个标记位的含义是仅仅匹配那些在intent-filter中声明了
在action和category中,有一类action和category比较特殊:
这二者共同作用是用来标注这是一个入口Activity并且会出现在系统的应用列表中,少了任何一个都没有实际意义,也无法出现在系统的应用列表中。