Intent知识详解

一、什么是Intent

贴一个官方解释:

An intent is an abstract description of an operation to be performed. It can be used with Context#startActivity(Intent) to launch an android.app.Activity , broadcastIntent to send it to any interested BroadcastReceiver components, and android.content.Context#startService or android.content.Context#bindService to communicate with a background android.app.Service .

An Intent provides a facility for performing late runtime binding between the code in different applications. Its most significant use is in the launching of activities, where it can be thought of as the glue between activities. It is basically a passive data structure holding an abstract description of an action to be performed.

Intent 名为意图,它是要执行操作的抽象描述,可以在activity、broadcast、service等组件进行请求操作时用来充当消息传递对象(intent可传递基础类型数据或者可序列化的对象数据)此时Intent包含要执行的动作的抽象描述。

二、分类

Intent主要分为两大类:显示Intent和隐式Intent。

显示Intent

定义 :显示的指定具体类名启动一个组件 一般用于同应用内的组件启动 因为可以方便的知道启动组件的类名

使用方式:1、通过Intent构造函数传入要启动类名

2、直接设置要启动类名(调用setComponent(), setClass(), setClassName()传入组件名)

隐式Intent

定义 :不知道要启动的组件名,通过匹配其他组件中manifest文件的Intent-filter,启动符合条件的组件,并把Intent中的参数传过去,一般用于启动外部应用的组件

使用方式:通过设置action、Category、data等去匹配符合条件的组件(注意:如果有多个intent-filter满足条件,那么系统会弹出一个对话框,由用户决定启动哪个组件 )

假如我们不希望其他应用启动我们的组件,只希望在本应用中使用组件,那么我们就不要在清单中声明,并且将该组件的 exported 属性设置为 false

三、intent工作流程

以Activity A启动Activity B为例说明:

1、首先Activity A调用startActivity()并传入的Intent

2、系统会根据该Intent的条件搜索Android系统中所有匹配的组件

3、若找到了匹配intent的intent-filters所属的组件(Activity B),则启动该组件,并回调onCreate()方法,同时将Intent传递过去

四、构建Intent

Intent的构建主要是为其设置各种属性包括:actiondatatypecomponentcategoryextrasflags。其中主要属性是actiondata 。下面我们来详细解析下每个属性的意义和作用。

action

action是指要执行的动作。它很大程度上决定了category和data中应传入的信息;除了官方定义的我们也可以自己定义action,以便让其他应用程序启动自己的组件。action可以通过setAction来设置或者在Intent构造函数中设置。

系统提供的常用Action:
public static final String ACTION_MAIN = “android.intent.action.MAIN”; //Android 的程序入口
public static final String ACTION_VIEW = “android.intent.action.VIEW”; //显示指定数据
public static final String ACTION_WEB_SEARCH = “android.intent.action.WEB_SEARCH”;//网页搜索关键字
public static final String ACTION_CALL = “android.intent.action.CALL”; //直接呼叫 Data 中所带的号码

data

data 属性通常是为 Action 属性提供要操作的数据,Data 属性的值是一个 Uri 对象,格式是:schema://host:port/path 。其各个字段含义如下:

* schema 协议 比如“http”、“https”、“tel”…

* host 主机名如“google.com”,如果定义为“*”则表示任意主机名

* port 端口号

* path 路径

可以通过setData设置data 。

type

Type 属性用于指定 Data 所制定的 Uri 对应的MIME 类型。MIME 类型是设定某种扩展名的 文件用一种应用程序来打开的方式类型。常见MIME类型 : “image/png”、”image/jpeg”

可以通过setType设置 type。

但是要注意的是如果您需要同时设置URI和MIME类型,只能调用setDataAndType()方法,而不能分别调用setData()和setType(),因为调用setData()时会首先将setType()中的内容置空,反之亦然。

component

component是要启动目标组件的名字。对于显式启动,这是不可缺省的,没有指定 ComponentName 属性的 Intent 被称为隐式 Intent。

可以通过调用setComponent(), setClass(), setClassName()等方法设置或者通过Intent的构造方法设置。

category

category是要执行动作的目标所具有的特质或行为归类(为 Action 增加额外的附加类别信息)

几个常见的category如下:

Intent.CATEGORY_DEFAULT(android.intent.category.DEFAULT)// 默认的category

Intent.CATEGORY_PREFERENCE(android.intent.category.PREFERENCE) //表示该目标Activity是一个首选项界面;

Intent.CATEGORY_BROWSABLE(android.intent.category.BROWSABLE)//指定了此category后,在网页上点击图片或链接时,系统会考虑将此目标Activity列入可选列表,供用户选择以打开图片或链接。

可以通过setCategory来进行设置。

flags

flags为intent添加元数据(meta-data),flag可以指导系统以何种方式启动一个activity、是否将启动的activity放在该应用的任务栈中,等等。

常用flags:

FLAG_ACTIVITY_NEW_TASK:设置这个标记位的话,是为 Activity 指定 “singleTask” 启动模式,它的作用和在清单文件中指定该启动模式的效果一样。
FLAG_ACTIVITY_SINGLE_TOP:设置这个标记位的话,是为 Activity 指定 “singleTop” 启动模式,它的作用和在清单文件中指定该启动模式的效果一样。
FLAG_ACTIVITY_CLEAR_TOP:具有此标记位的 Activity ,在它启动时,在同一个任务栈中所有位于它上面的 Activity 都要出栈。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有这个标记的 Activity 不会出现在历史 Activity 的列表中。它等同于在清单文件中指定 Activity 的属性 android:excludeFromRecents=“true”

可以通过setFlags来进行设置。

extras

extras用于添加一些附加信息,它的属性值是一个 Bundle 对象,通过键值对的形式存储数据。

需要注意的是在使用putExtras方法设置Bundle对象之后,系统进行的不是引用操作,而是复制操作,所以如果设置完之后再更改bundle实例中的数据,将不会影响Intent内部的附加信息。

可以通过putExtras来进行设置。

Intent属性小结

Component name, action, data, and category代表了intent的属性,通过设置这些参数,系统可以筛选出符合条件的目标组件。但是,Extras、Flags这两个参数系统不会用来筛选目标组件。

五、IntentFilter匹配规则

IntentFilter是manifest文件中组件内部的一个标签,该标签描述了组件具备什么特性,如果您未配置intent-filters,那个该组件只能被显式启动。我们在mainfest中设置的ntent-filters如果可以匹配某个隐式Intent那么该组件就可以被启动。IntentFilter在做匹配时主要是根据action, type, category这三个属性且匹配优先级是:action>data>category

action匹配规则:

如果Intent指明定了action,则目标组件的IntentFilter的action列表中就必须包含有这个action,否则不能匹配。一个Intent Filter中可声明多个action,此时Intent中的action与其中的任一个action在字符串形式上完全相同即可匹配成功。

特殊情况:

如果filter中没有设置任何action 那么所有的intent匹配都会失败

如果action只和category组合使用(隐式调用的条件),intent中不指定action,那么无法启动目标组件

如果action和category、data组合使用,intent中不指定action但是filter中至少存在一个action 那么是可以匹配成功的

category的匹配规则:

Intent-filter可定义零到多个category标签intent中的定义的每一个category都需要匹配上intent-filter中的category标签,反之不成立(intent-filter中的category标签可能比intent中的定义的category多)。所以无论intent-filter中是否定义了category标签,未添加category的intent总能匹配上该intent-filter。

注意:

通过startActivity()或startActivityForResult()方法隐式启动的intent中,将自动被添加一个CATEGORY_DEFAULT的category,所以若您希望自己的activity能够被隐式启动,则需要在intent-filter中添加一个android.intent.category.DEFAULT的category标签。

data匹配规则:

intent filter可定义零到多个data标签每个data标签都能设置mimeType和URI 结构,其中URI可分成四部分:scheme, host, port 和 path。但是是有一个线性依赖:若scheme 未指定,则host被忽略;若host未指定,则port被忽略;

若scheme和host均未指定,则path被忽略;

在intent中添加的data只需要匹配一部分intent-filter中的data:

  • 若filter只定义了scheme,则intent的data定义的URI中只要包含了相同的scheme,就能匹配;
  • 若filter只定义了scheme和host,则intent的data定义的URI中只要包含了相同的scheme和host,就能匹配;
  • 若filter只定义了scheme、host和port,则intent的data定义的URI中只要包含了相同的scheme、host和port,就能匹配

六、Intent源码解析

Intent的源码大约1W行其中对我们有用打大概有以下几类:

构造函数

public Intent() {
}
public Intent(String action) {
setAction(action);
}
public Intent(String action, Uri uri) {
setAction(action);
mData = uri;
}

Intent提供了8中构造函数供我们使用。

属性的set和get方法

public @Nullable Uri getData() {
return mData;
}
public @Nullable String getType() {
return mType;
}
public @NonNull Intent setData(@Nullable Uri data) {
mData = data;
mType = null;
return this;
}
public @NonNull Intent setType(@Nullable String type) {
mData = null;
mType = type;
return this;
}
public @NonNull Intent setDataAndType(@Nullable Uri data, @Nullable String type) {
mData = data;
mType = type;
return this;
}
//...

此处我们看到下setData/Type,单独调用都会把对方置空,所以如果你想设置data和type需要调用setDataAndType,而不能先调用其中一个然后再调用另一个。

此外还有putExtra等相关函数这里就不做分析了,有兴趣的可自行查看源码。

最后看一个平时我们并不常用的函数toUri:

public String toUri(@UriFlags int flags) {
StringBuilder uri = new StringBuilder(128);
if ((flags&URI_ANDROID_APP_SCHEME) != 0) {
if (mPackage == null) {
throw new IllegalArgumentException(
"Intent must include an explicit package name to build an android-app: "
+ this);
}
uri.append("android-app://");
uri.append(mPackage);
String scheme = null;
if (mData != null) {
scheme = mData.getScheme();
if (scheme != null) {
uri.append('/');
uri.append(scheme);
String authority = mData.getEncodedAuthority();
if (authority != null) {
uri.append('/');
uri.append(authority);
String path = mData.getEncodedPath();
if (path != null) {
uri.append(path);
}
String queryParams = mData.getEncodedQuery();
if (queryParams != null) {
uri.append('?');
uri.append(queryParams);
}
String fragment = mData.getEncodedFragment();
if (fragment != null) {
uri.append('#');
uri.append(fragment);
}
}
}
}
toUriFragment(uri, null, scheme == null ? Intent.ACTION_MAIN : Intent.ACTION_VIEW,
mPackage, flags);
return uri.toString();
}
String scheme = null;
if (mData != null) {
String data = mData.toString();
if ((flags&URI_INTENT_SCHEME) != 0) {
final int N = data.length();
for (int i=0; i<N; i++) {
char c = data.charAt(i);
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|| c == '.' || c == '-') {
continue;
}
if (c == ':' && i > 0) {
// Valid scheme.
scheme = data.substring(0, i);
uri.append("intent:");
data = data.substring(i+1);
break;
} // No scheme.
break;
}
}
uri.append(data); } else if ((flags&URI_INTENT_SCHEME) != 0) {
uri.append("intent:");
} toUriFragment(uri, scheme, Intent.ACTION_VIEW, null, flags); return uri.toString();
}

它的作用是把一个Intent转化为一个Uri,转化后的Uri包含原先Intent的action, categories, type, flags, package, component, and extras等属性。同时Intent还提供了getIntent方法把Uri转换回Intent。

Intent转换Uri过程就是将Intent的属性值读取出来进行拼接然后序列化。不过需要注意的是它没有关于 Bundle 的参数传递,所以转换过程会把设置的bundle数据丢失。

利用该函数我们可以不必在调用startActivity前去new Intent而是通过getIntent把一个Uri转化为Intent然后再startActivity,这样做的好处是startActivity中传入的intent变为”可控”的。

七、相关问题

path、pathPrefix、pathPattern 之间的区别

path 用来匹配完整的路径,如:http://example.com/blog/abc.html,这里将 path 设置为 /blog/abc.html 才能够进行匹配;

pathPrefix 用来匹配路径的开头部分,拿上来的 Uri 来说,这里将 pathPrefix 设置为 /blog 就能进行匹配了;

pathPattern 用表达式来匹配整个路径,这里需要说下匹配符号与转义。

匹配符号:

“” 用来匹配0次或更多,如:“a” 可以匹配“a”、“aa”、“aaa”…

“.” 用来匹配任意字符,如:“.” 可以匹配“a”、“b”,“c”…

因此 “.” 就是用来匹配任意字符0次或更多,如:“.html” 可以匹配 “abchtml”、“chtml”,“html”,“sdf.html”…

Intent传递数据的大小限制

intent传递过大的数据会导致TransactionTooLargeException,其本质原因是intent使用binder进行数据传递。在过程中Intent 中的数据,会作为 Parcel 被存储在 Binder 的事务缓冲区(Binder transaction buffer)中的对象进行传输。但是Binder 的事务缓冲区大小为1M,并且该缓冲区是进程共享的。

解决方法:

1、避免传递过大数据

2、使用 EventBus 的粘性事件来解决

查询是否有Activity可以匹配我们指定Intent的组件

在启动Activity时传入intent找不到符合条件的Activity那么程序将会崩溃,所以我们每次startActivity时最好查询下是否有Activity可以匹配我们指定Intent的组件,可以使用以下方法:

  • PackageManager的resolveActivity或者Intent的resolveActivity方法会获得最适合Intent的一个Activity
  • 调用PackageManager的queryIntentActivities会返回所有成功匹配Intent的Activity

android.intent.action.MAIN 与android.intent.category.LAUNCHER、android.intent.category.HOME的区别

android.intent.action.MAIN

应用的入口即最先启动的组件

android.intent.category.LAUNCHER

决定是否在桌面显示图标

android.intent.category.HOME

按住“HOME”键,该程序显示在HOME列表里

有多个匹配组件时如何设置每次都弹窗

当有多个应用可以响应我们的隐式 Activity 时,系统会弹出一个选择框,让用户选择需要打开的应用,用户也可以选择记住要自己打开的应用,这样下次就不会再弹出选择框。那么假如我希望每次都弹窗,不让用户记住呢?我们可以使用 createChooser() 创建 Intent

参考文章:

Intent知识详解的更多相关文章

  1. Context知识详解

    Context知识详解 建议配合context知识架构图食用. 一.什么是Context 贴一个官方解释: Interface to global information about an appli ...

  2. RabbitMQ基础知识详解

    什么是MQ? MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.MQ是消费-生产者模型的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取队列中 ...

  3. Cisco路由技术基础知识详解

    第一部分 请写出568A的线序(接触网络第一天就应该会的,只要你掐过,想都能想出来) .网卡MAC地址长度是(  )个二进制位(16进制与2进制的换算关系,只是换种方式问,不用你拿笔去算) A.12  ...

  4. L009文件属性知识详解小节

    本堂课分为5部分内容 1.linux下重要目录详解 2.PATH变量路径内容 3.linux系统中文件类型介绍 4.linux系统中文件属性详细介绍 5.linux系统文件属性inode与block知 ...

  5. RabbitMQ,Apache的ActiveMQ,阿里RocketMQ,Kafka,ZeroMQ,MetaMQ,Redis也可实现消息队列,RabbitMQ的应用场景以及基本原理介绍,RabbitMQ基础知识详解,RabbitMQ布曙

    消息队列及常见消息队列介绍 2017-10-10 09:35操作系统/客户端/人脸识别 一.消息队列(MQ)概述 消息队列(Message Queue),是分布式系统中重要的组件,其通用的使用场景可以 ...

  6. 浏览器对象模型(BOM)是什么?(体系结构+知识详解)(图片:结构)

    浏览器对象模型(BOM)是什么?(体系结构+知识详解)(图片:结构) 一.总结 1.BOM操作所有和浏览器相关的东西:网页文档dom,历史记录,浏览器屏幕,浏览器信息,文档的地址url,页面的框架集. ...

  7. Android零基础入门第80节:Intent 属性详解(下)

    上一期学习了Intent的前三个属性,本期接着学习其余四个属性,以及Android系统常用内置组件的启动. 四.Data和Type属性 Data属性通常用于向Action属性提供操作的数据.Data属 ...

  8. Python字符串切片操作知识详解

    Python字符串切片操作知识详解 这篇文章主要介绍了Python中字符串切片操作 的相关资料,需要的朋友可以参考下 一:取字符串中第几个字符 print "Hello"[0] 表 ...

  9. Python基础知识详解 从入门到精通(七)类与对象

    本篇主要是介绍python,内容可先看目录其他基础知识详解,欢迎查看本人的其他文章Python基础知识详解 从入门到精通(一)介绍Python基础知识详解 从入门到精通(二)基础Python基础知识详 ...

随机推荐

  1. ArcGIS Server10.4安装教程

    准备内容 安装环境:win10*64位专业版 安装文件:ArcGIS_Server_Ent_Windows_1041_150998.iso 破解文件:Lic10.4.1.ecp #安装Server前, ...

  2. python线程条件变量Condition(31)

    对于线程与线程之间的交互我们在前面的文章已经介绍了 python 互斥锁Lock / python事件Event , 今天继续介绍一种线程交互方式 – 线程条件变量Condition. 一.线程条件变 ...

  3. python3 之 文件read方法(read、readline、readlines)

    目录 一.read方法 二.readline方法 三.readlines方法 正文 python3中,读取文件有三种方法:read().readline().readlines(). 此三种方法,均支 ...

  4. pycham永久激活及conda环境部署

    1.pycham安装 一般不选择最新版本,我用的是2018.3,选择 Professional专业版 1.1 官网地址: https://www.jetbrains.com/pycharm/downl ...

  5. 2019-9-9:渗透测试,基础学习,pydictor使用,sql盲注,docker使用,笔记

    pydictor,强大的密码生成工具,可以合并密码字典,词频统计,去重,枚举数字字典生成字典python3 pydictor.py -base d --len 4 4 生成纯数字4位密码python3 ...

  6. 【Luogu P4779】dijkstra算法的堆优化

    Luogu P4779 利用堆/优先队列快速取得权值最小的点. 在稠密图中的表现比SPFA要优秀. #include<iostream> #include<cstdio> #i ...

  7. github上传文件让别人下载--xdd

    一.可以下载的条件 仓库要为公开(public) 该文件不可预览或者是图片,如.rar  .gif .png .doc .pdf 等格式 二.打开文件的预览界面,如下 三.将最上面的地址复制给别人即可 ...

  8. CSS中如果实现元素浮动,看这篇文章就足够了

    浮动基本介绍 在标准文档流中元素分为2种,块级元素和行内元素,如果想让一些元素既要有块级元素的特点也同时保留行内元素特点,只能让这些元素脱离标准文档流即可. 浮动可以让元素脱离标准文档流,可以实现让多 ...

  9. Lambda入门,看这一篇幅就够了

    jdk1.8中的lambda表达式学习笔记 一.引入一个例子 我们写一个多线程的例子,如下:采用实现Runable接口的方式 package cn.lyn4ever.lambda; public cl ...

  10. 基于 HTML5 + WebGL 的太阳系 3D 展示系统

    前言 近年来随着引力波的发现.黑洞照片的拍摄.火星上存在水的证据发现等科学上的突破,以及文学影视作品中诸如<三体>.<流浪地球>.<星际穿越>等的传播普及,宇宙空间 ...