Intent在实际工作的使用率可以说和四大组件一样的多
什么是Intent? ⭐⭐⭐⭐⭐
显式Intent和隐式Intent的区别?⭐⭐⭐⭐
在隐式启动中Intent可以设置多个action,多个category吗?⭐⭐⭐⭐
隐式Intent的匹配规则?⭐⭐⭐⭐⭐
Activity之间传递数据的方式Intent是否有大小限制,如果传递的数据量偏大,有哪些方案?⭐⭐⭐
目录
1、什么是Intent
1.1 显示Intent
1.2 隐式Intent
1.3 Intent的组成部分
1.3.1 componentName
1.3.2 action
1.3.3 category
1.3.4 data
1.3.5 type
1.3.6 extras
1.3.7 flags
1.4 隐式Intent匹配规则总结
1.5 显示Intent与隐式Intent的区别
2、拓展
2.1 Intent携带数据的大小
2.2 PendingIntent
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 |
在清单文件中的里,只有<data>没有<type>。但在代码里就可以调用:
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和清单文件中使用 <intent-filter> 元素为组件声明一个或多个 Intent 过滤器的、、几个属性做匹配,找到最合适的。总结如下:
清单文件中:category如要使用默认值,则设为android.intent.category.DEFAULT,否者组件不会接收隐式 Intent;
如果不希望自己的组件,如自定义的XXXActivity被其他应用程序调用,则清单文件中,XXXActivity下面不要声明 <intent-filter> ,并且将该组件的 exported 属性设置为 false;
代码中:如果没有指定category,系统会默认设置为android.intent.category.DEFAULT;
一个Intent只有一个action,但可以有多个category,只有当action和所有category和某个<intent-filter>都同时匹配,才算匹配成功。
一个<intent-filter>可以设置一个或多个<action>,当Intent能和<intent-filter>的任意一个action匹配就算匹配成功;
一个<intent-filter>可以设置一个或多个<category>,只有当Intent能和<intent-filter>的所有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的具体用法和实现原理。