Intent封装了Android应用启动某个组件(Activity、Service、BroadcastReceiver)的“意图”,不仅如此,Intent还是各个组件之间通信的重要媒介(使用Intent携带Bundle对象)
使用Intent启动不同组件的方法:startActivity、startActivityForResult、startService、bindService、sendBroadcast
Intent对象大致包含Component、Action、Category、Data、Type、Extra和Flag这七种属性,其中,Component用于明确指定需要启动的目标组件,而Extra则用于携带需要交换的数据
Intent的Component属性需要接受一个ComponentName对象,ComponentName对象包含如下几个构造器:
ComponentName(String pkg,String cls)
ComponentName(Context pkg,String cls)
ComponentName(Context pkg,Class<?> cls)
上面构造器的本质就是:创建一个ComponentName需要指定包名和类名,这就可以唯一确定一个组件类,除此之外,Intent还包含了如下三个方法:setClass、setClassName、setClassName
指定Component属性的Intent已经明确了它将要启动哪个组件,因此这种Intent也被称为显示Intent,否则称为隐式Intent
ComponentName comp = new ComponentName(a.this,b.class);
Intent intent = new Intent();
intent.setComponent(comp);
可以简化为:Intent intent = new Intent(a.this,b.class);
Intent的Action、Category属性都是一个普通的字符串,其中Action代表该Intent所要完成的一个抽象动作,而Category则用于Action增加额外的附加类别信息,通常Action属性会与Category属性结合使用,最终启动哪个组件,取决于组件配置的<intent-filer/>元素,<intent-filter/>元素里通常包含如下子元素:
0到N个<action/>子元素
0到N个<category/>子元素
0到1个<data/>子元素
一个Intent对象最多只能包含一个Action属性,可以通过Intent的setAction方法来设置,但一个Intent对象可以包含多个Category属性,通过Intent的addCategory方法来添加,当程序创建Intent时,该Intent默认启动Category属性值为Intent.CATEGORY_DEFAULT常量(android.intent.category.DEFAULT)
Intent代表了启动某个组件的“意图”,实际上Intent对象不仅可以启动本应用内组件,也可以启动Android系统的其他应用的组件,其中包括系统自带的组件,Android内部提供了大量标准Action、Category常量
startActivity -- intent(Intent.ACTION_MAIN、Intent.CATEGORY_HOME)返回桌面
Data属性通常用于向Action属性提供操作的数据,Data属性接受一个Uri对象,一个Uri对象通常通过如下形式的字符串来表示:scheme://host:port/path
Type属性用于指定该Data所指定Uri对应的MIME类型,这种MIME类型可以是任何自定义的MIME类型,只要符合abc/xyz格式的字符串即可
Data属性与Type属性会相互覆盖,如果希望Intent既有Data属性,也有Type属性,应该调用Intent的setDataAndType方法
在Manifest文件中为组件声明Data、Type属性都通过<data/>元素,mimeType用于声明该组件所能匹配的Intent的Type属性,scheme用于声明该组件所能匹配的Intent的Data属性的scheme部分,host、port、path、pathPrefix、pathPattern
Intent的Type属性必须与<data/>子元素的mimeType属性相同,才能启动该组件
Intent的Data属性并不要求android:scheme、android:host、android:port、android:path完全匹配
只有Intent的action、category、data属性完全匹配才能成功启动目标Activity。过滤规则中可以有多个action,而Intent中的action最多只能有一个,只要Intent中的action能够匹配过滤规则中的其中一个action即匹配成功,Intent中可以没有category,但是如果你一旦有category,不管有几个,每个都要能够匹配过滤规则中的其中一个category
data由两部分组成,mimeType和URI,如果URI中没有指定scheme,那么整个URI的其他参数无效,如果host未指定,那么整个URI中的其他参数无效,仅当URI中指定了scheme和host时port参数才有意义,另外,data匹配过滤规则是全匹配的,比如filter中设置了port信息,但是intent中没有设置port信息,则匹配不成功
下面两种写法,作用相同
<data android:scheme=“file”android:host=”www.baidu.com”/>
<data android:scheme=“file”/>
<data android:host=”www.baidu.com”/>
Extra属性通常用于在多个Action之间进行数据交换,Intent的Extra属性值应该是一个Bundle对象,Buidle对象就像一个Map对象,它可以存入多组key-value对
Flag属性:Intent.FLAG_ACTIVITY_CLEAR_TOP、Intent.FLAG_ACTIVITY_NEW_TASK
Intent.FLAG_ACTIVITY_SINGLE_TOP:与加载模式singleTop功能相同
Intent.FLAG_ACTIVITY_CLEAR_TOP销毁目标Activity和它之上的所有Activity,重新创建目标Activity
Intent.FLAG_ACTIVITY_NEW_TASK
来个面试题:如何通过 singleTop 实现 singleTask
解法1
FLAG_ACTIVITY_NEW_TASK 与 FLAG_ACTIVITY_CLEAR_TOP 的理解纠正
- FLAG_ACTIVITY_NEW_TASK 不等价于启动模式 singleTask,它仅表示寻找 Activity 所需的任务栈压入
- FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TOP 也不等价于 singleTask 启动模式
- 在 FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TOP 的情况下,如果 Activity 的启动模式为 standard 或 singleTask 时 Activity 入栈方式是不一样的,分为如下三种情况
- 当启动模式为 standard 时,如果 Activity 所需的栈中已经存在该 Activity 的实例了,那么这个实例连同它之上的 Activity 都要出栈,然后再新建一个 Activity 实例入栈
- 当启动模式为 singleTask 时,如果 Activity 所需的栈中已经存在该 Activity 的实例了,那么系统会调用该实例的 onNewIntent() 方法,且只将该实例之上的 Activity 出栈
- 如果 Activity 所需的栈中不存在该 Activity 的实例,则不论启动模式为 standard 还是 singleTask,都是新建 Activity 实例直接入栈
解法2
val activityManager = this.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val activityTasks: ArrayList<ActivityManager.AppTask> =
activityManager.appTasks as ArrayList<ActivityManager.AppTask>
activityTasks.forEach {
it.taskInfo
}
activityTasks[0].taskInfo.taskId