Intent
Intent
1、什么是Intent
Intent,中文可翻译为“意图”,可用于Android同个应用程序中各个组件之间的交互,或者不同应用程序之间的交互。可以用来表明当前组件的思想和意图,比如想执行某个动作,想发送某些数据等等。每个组件都有不同的启动方法:
- Activity:可以调用startActivity() 或 startActivityForResult() 传递 Intent来打开新的Activity;
- Service:可以调用startService()传递 Intent 来启动服务,也可通过 bindService() 传递 Intent 来绑定到该服务;
- Broadcast,可以调用sendBroadcast()、sendOrderedBroadcast() 或 sendStickyBroadcast() 等方法传递 Intent 来发起广播;
Intent分为显式Intent和隐式Intent,我们以打开新的Activity为例进行讲解。
1.1 显示Intent
显式指定意图,系统就会根据我们指定的意图,打开我们想打开的Activity。Intent有如下构造函数:Intent(Context packageContext, Class<?> cls)。其中第二个参数就是指定想启动的Activity。
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
通过上面两行代码,我们可以显式指定打开SecondActivity.class。
1.2 隐式Intent
不指定特定的意图,而是通过清单文件里,每个Activity节点下事先配置好的(也就是“Intent过滤器”),由系统根据我们设定的Intent,从系统所有Activity中选出最符合的我们要求的Activity。先看看例子:
// 清单文件中 XuruiActivity 提前声明好如下:
<activity android:name=".XuruiActivity">
<intent-filter>
<action android:name="com.example.android.xuruitest"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="com.example.android.xuruicategory"/>
</intent-filter>
</activity>
// 代码调用
Intent intent = new Intent();
intent.setAction(com.example.android.xuruitest);
intent.addCategory(com.example.android.xuruicategory);
startActivity(intent); //1
执行 [注释1]的代码后,系统会发现XuruiActivity的所设定的内容,和当前Intent所设定的内容最匹配,系统就会打开XuruiActivity,但这个过程中,我们并没有显式的指出打开XuruiActivity,而是通过设置了一些特定条件进行匹配,如“action”,“category”等,从而隐式地打开了XuruiActivity。
1.3 Intent的组成部分
“action”,“category”都是Intent的组成部分。为了更好的理解隐式Intent,需要了解一个Intent由几部分组成:
- componentName:目的组件
- action(动作):用来表现意图的行动
- category(类别):用来表现动作的类别
- data(数据):表示与动作要操纵的数据
- type(数据类型):对于data范例的描写
- extras(扩展信息):扩展信息
- Flags(标志位):期望这个意图的运行模式
1.3.1 componentName
指定Intent目标组件的类名。可以这么记得,如果直接指定了componentName,那就是显式Intent。我们可以通过setComponent()、setClass()、setClassName()等方法指定:
// 1、Intent构造
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
// 2、setComponent()方法
ComponentName componentName = new ComponentName(this, SecondActivity.class); //2
ComponentName componentName = new ComponentName(this, "com.example.android.SecondActivity"); //3
ComponentName componentName = new ComponentName(this.getPackageName(), "com.example.android.SecondActivity"); //4
Intent intent = new Intent();
intent.setComponent(componentName);
startActivity(intent);
// 3、setClass/setClassName方法
Intent intent = new Intent();
intent.setClass(this, SecondActivity.class);
intent.setClassName(this, "com.example.android.SecondActivity");
intent.setClassName(this.getPackageName(), "com.example.android.SecondActivity");
startActivity(intent);
我们看看[注释2-4]这三种方式:
- [注释2]:this是当前Activity,直接写类名,则该Activity必须在同个包名内;
- [注释3]:this是当前Activity,类名包括包名,则可以在当前包跳转到其他包的Activity;
- [注释4]:this是当前Activity所在的应用程序,类名包括包名,则可以在当前包跳转到其他包的Activity;
在可以使用应用的Context代替Activity的Context时,都推荐使用应用的Context,且Activity名称前面加上包名可以打破同个包内的限制,所以推荐使用[注释4]的方式。
1.3.2 action
用来表示意图的行动,一个Intent只能有一个Action。以下是一些比较常见的Action。所有的Action可以在Android开发手册-Intent 查看。
Action | 含义 |
---|---|
ACTION_MAIN | Android 的程序入口 |
ACTION_VIEW | 显示指定数据 |
ACTION_EDIT | 编辑指定数据 |
ACTION_DIAL | 显示拨号面板 |
ACTION_CALL | 直接呼叫 Data 中所带的号码 |
ACTION_ANSWER | 接听来电 |
ACTION_SEND | 向其他人发送数据(例如:彩信/email) |
打开手机的拨号界面的代码可以这么写:
Intent intent=new Intent();
intent.setAction(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:1234567"));
startActivity(intent);
1.3.3 category
用来表示动作的类别,一个Intent可以有多个category,类别越多,代表动作越具体,意图越明确。
1.3.4 data
清单文件里标签具体如下:
属性 | 解释 |
---|---|
scheme | 协议 |
host | 主机 |
port | 端口 |
path | 用来匹配完整的路径 |
pathPrefix | 用来匹配路径的开头部分 |
pathPattern | 用表达式来匹配整个路径 |
mimeType | 用来匹配数据类型或MIME类型 |
举个例子:
<data android:scheme="http" android:host="www.baidu.com"/>
系统内置的几个 Data 属性常量:
协议 | 含义 |
---|---|
tel: | 号码数据格式,后跟电话号码 |
mailto: | 邮件数据格式,后跟邮件收件人地址 |
smsto: | 短信数据格式,后跟短信接收号码 |
file:/// | 文件数据格式,后跟文件路径。注意必须是三根斜杠 /// |
content:// | 内容数据格式,后跟需要读取的内容。ContentProvider 特有的格式 |
1.3.5 type
type 属性用于指定 data 所制定的 Uri 对应的 MIME 类型,通常应用于调用系统 App,比如实现查看文件(文本、图片、音频或者视频等),通过指定文件的 MIME 类型,可以让系统知道用什么程序打开该文件。
常用的 MIME 类型:
文件格式 | 对应的MIME类型 |
---|---|
.bmp | image/bmp |
.gif | image/gif |
.png | image/png |
.tif .tiff | image/tiff |
.jpe .jpeg .jpg | image/jpeg |
.txt | text/plain |
.xml | text/xml |
.html | text/html |
.css | text/css |
.js | text/javascript |
.mht .mhtml | message/rfc822 |
.doc | application/msword |
.docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document |
.rtf | application/rtf |
.xls | application/vnd.ms-excel application/x-excel |
.xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
.ppt | application/vnd.ms-powerpoint |
.pptx | application/vnd.openxmlformats-officedocument.presentationml.presentation |
.pps | application/vnd.ms-powerpoint |
.ppsx | application/vnd.openxmlformats-officedocument.presentationml.slideshow |
application/pdf | |
.swf | application/x-shockwave-flash |
.dll | application/x-msdownload |
.exe | application/octet-stream |
.msi | application/octet-stream |
.chm | application/octet-stream |
.cab | application/octet-stream |
.ocx | application/octet-stream |
.rar | application/octet-stream |
.tar | application/x-tar |
.tgz | application/x-compressed |
.zip | application/x-zip-compressed |
.z | application/x-compress |
.wav | audio/wav |
.wma | audio/x-ms-wma |
.wmv | video/x-ms-wmv |
.mp3 .mp2 .mpe .mpeg .mpg | audio/mpeg |
.rm | application/vnd.rn-realmedia |
.mid .midi .rmi | audio/mid |
在清单文件中的里,只有没有
- setType(): 调用后设置 mimeType,然后将 data 置为 null;
- setData(): 调用后设置 data,然后将 mimeType 置为 null;
- setDataAndType(): 调用后才会同时设置 data 与 mimeType。
举个打开百度的例子:
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="http" android:host="www.baidu.com"/>
</intent-filter>
intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri data=Uri.parse("http://www.baidu.com");
intent.setData(data);
startActivity(intent);
1.3.6 extras
extras:拓展信息,通常用于多个Activity之间的数据交换,extras属性是一个Bundle对象,通过键值对,对数据进行存储。extras在日常开发中非常常见:
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("name", "Tony");
intent.putExtra("age", 28);
startActivity(intent);
1.3.7 flags
flags:标志位,指明当前的Intent所期望的运行模式,具体有:
- FLAG_ACTIVITY_CLEAR_TOP:新跳转的Activity如果已经在栈中,则将其上面的所有Activity都清出栈,相当于Activity四大启动模式中的singleTask;
- FLAG_ACTIVITY_SINGLE_TOP:返回栈没有当前需要跳转的Activity,则新建该Activity并放入栈顶。但如果该Activity已经在栈顶了,则不会再重新创建新的Activity。相当于Activity四大启动模式中的singleTop;
- FLAG_ACTIVITY_NEW_TASK:假设当前返回栈有A B C这3个Activity。此时C通过intent跳转到D,并且这个intent添加了FLAG_ACTIVITY_NEW_TASK标记,如果D这个Activity在AndroidManifest.xml中的声明中添加了Task affinity,系统首先会查找有没有和D的Task affinity相同的task栈存在,如果有存在,将D压入那个栈,如果不存在则会新建一个D的affinity的栈将其压入。但如果D的Task affinity默认没有设置,则会把其压入原来的返回栈,变成:A B C D,这样就和不加FLAG_ACTIVITY_NEW_TASK标记效果是一样的了。注意如果试图从非activity的非正常途径启动一个activity,比如从一个service中启动一个activity,则intent比如要添加FLAG_ACTIVITY_NEW_TASK标记。
1.4 隐式Intent匹配规则总结
隐式Intent的匹配规则,就是根据当前Intent所设置的action、category、data和清单文件中使用
- 清单文件中:category如要使用默认值,则设为android.intent.category.DEFAULT,否者组件不会接收隐式 Intent;
- 如果不希望自己的组件,如自定义的XXXActivity被其他应用程序调用,则清单文件中,XXXActivity下面不要声明
,并且将该组件的 exported 属性设置为 false; - 代码中:如果没有指定category,系统会默认设置为android.intent.category.DEFAULT;
- 一个Intent只有一个action,但可以有多个category,只有当action和所有category和某个
都同时匹配,才算匹配成功。 - 一个
可以设置一个或多个 ,当Intent能和 的任意一个action匹配就算匹配成功; - 一个
可以设置一个或多个 ,只有当Intent能和 的所有category匹配才算匹配成功; - 如果有多个组件被匹配成功,就会以对话框列表的方式让用户进行选择。
1.5 显示Intent与隐式Intent的区别
对明确指出了目标组件名称的Intent,我们称之为“显式Intent”。 对于没有明确指出目标组件名称的Intent,则称之为“隐式 Intent”。
2、拓展
2.1 Intent携带数据的大小
Intent传递数据大小的限制大概在1M左右,超过这个限制就会静默崩溃。所以,如果是同个应用程序之间的数据传输,可以使用文件缓存或者磁盘缓存,如果是应用程序之间,则推荐使用ContentProvider进行数据传输。
2.2 PendingIntent
PendingIntent就是延迟或者挂起的Intent,平时也会使用到,在此做个拓展。如接收到消息需要在桌面做通知时,可以这么写:
pendingIntent = PendingIntent.getActivities(
applicationContext, 0, intents,
PendingIntent.FLAG_UPDATE_CURRENT
)
val builder = NotificationCompat.Builder(applicationContext, FORCE_UPDATE_NOTIFY_CHANNEL_ID)
builder.setWhen(System.currentTimeMillis())
.setContentTitle(showTitle)
.setContentText(showContent)
.setContentIntent(pendingIntent) //5
.setPriority(NotificationCompat.PRIORITY_MAX)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setVisibility(VISIBILITY_PUBLIC)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
其中,[注释5]的pendingIntent就是PendingIntent。PendingIntent的具体用法和实现原理,推荐看看这篇文章:Android 面试题:说一下 PendingIntent 和 Intent 的区别