什么是Intent
intent是一个消息传递对象,可以使用它从其他应用组件请求操作。通常我们用intent来启动一个activity,服务或者是广播,
启动activity
使用startactivity() 来启动一个activity。intent则封装了要启动的activity的信息以及传递的数据。如果你希望在activity完成后收到结果,你需要调用startactivityforresult()方法,在activity的onactivityresult()回调中的intent返回了数据内容
启动服务
service是一个后台执行操作的组件,他没有用户界面,通过startservice打开一个服务
传递广播
广播是任何应用都可以接受的消息,通过sendbroadcast()传递广播
就拿发送email举例吧,发送email必要条件:发送者,接受者,和载体(就是email本身)。intent类似于email,他作为一个传递对象封装了要传递的信息和内容,再由发送者(调用startactivity的界面)发送给接收者。
Intent 过滤器
intent过滤器是应用的清单文件中的一个表达式,他制定该组件要接收的intent类型,比如:通过给activity声明intent过滤器,可以使其他应用能够直接使用某一特定类型的intent来启动activity,如果没有给activity声明过滤器就不能通过隐式意图来启动,只能通过显式意图来启动activity
Intent的类型
intent的类型的类型有两种,一种叫显式意图,一种叫隐式意图
显式意图:通常在自己的程序中使用,明确知道我们要做什么事情,启动什么组件。
隐式意图:通常是调用第三方程序执行,我们不知道组件的名称,但是知道要执行的操作,制定匹配规则,让满足规则的程序来执行。
创建隐式意图时,android系统通过将intent的内容与设备上其他应用清单文件声明的intent过滤器进行比较,从而找到要启动的对应的组件,如果intent与ent过滤器匹配,则系统降启动这个组件,同事传递intent对象给组件, 如果有多个intent过滤器相匹配则系统会显示一个列表对话框,显示所有匹配的组件供用户自行选择要使用的应用。
创建一个显式意图
Intent intent =new Intent(); 创建意图对象
intent.setClass(c, ScanActivity.class); 设置要打开的目标
intent.putExtra("title", title); 给意图填充数据
intent.putExtra("hint", hint);
intent.putExtra("typeName", typeName);
activity.startActivity(intent); 开启
创建一个隐式意图intent
创建隐式意图要注意的是,如果手机没有任何应用来处理你的隐式意图,则调用失败应用会崩掉,所以在这之前要调用intent.resolveActivity()方法判断是否有应用匹配规则,如果返回非空,则至少有一个应用能够处理该意图,则可以调用,否则不应该调用
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND); 表明你要做什么事情
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage); 目标需要的内容
sendIntent.setType("text/plain"); 数据类型
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);}
接收隐式意图
要想应用能够接收隐式意图,必须在清单文件中使用<intent-filter>元素为组件声明过滤器。<intent-filter>元素应该嵌入对应的组件中,通常为activity。我们可以为组件设置一个或者多个过滤器,每个过滤器均需要根据意图操作,数据和类别指定自身接收的类型,当隐式意图满足过滤器规则时,系统就会将该意图传递给应用组件。
<intent-filter>元素的子元素
<action>
在那么属性中,声明接受的意图操作,钙质必须为操作的文本字符串值,而不是常量类。
<action android:name="android.intent.action.SEND"> name属性的值可以自定义,也可以使用安卓系统提供的一些默认值
<data>
使用一个或者多个指定数据URI各个方面(scheme,host,port,path等)和MIME类型的属性,声明接受的数据类型
<data android:mimeType="text/plain"/>
每个data分为MIME 和scheme。scheme包含host,port,path等。
MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的因特网标准。
<category>
在name属性中,声明接受的意图类别,钙质必须是操作文本字符串值,而不是常量类。
<category android:name="android.intent.category.DEFAULT>
为了接受隐式意图,必须将 category.DEFAULT 类别包括在过滤器中,如果没有在过滤器声明此类别,则隐式意图不会解析该组件
Intent Filter匹配规则
Intent解析机制主要是通过查找已注册在AndroidManifest.xml中的所有IntentFilter及其中定义的Intent,最终找到匹配的Intent。在这个解析过程中,Android是通过Intent的action、type、category这三个属性来进行匹配判断的。一个过滤列表中的action、type、category可以有多个,所有的action、type、category分别构成不同类别,同一类别信息共同约束当前类别的匹配过程。只有一个Intent同时匹配action、type、category这三个类别才算完全匹配,只有完全匹配才能启动Activity。另外一个组件若声明了多个Intent Filter,只需要匹配任意一个即可启动该组件。
action是一个字符串,如果Intent指明定了action,则目标组件的IntentFilter的action列表中就必须包含有这个action,否则不能匹配。一个Intent Filter中可声明多个action,Intent中的action与其中的任一个action在字符串形式上完全相同(注意,区分大小写,大小写不同但字符串内容相同也会造成匹配失败),action方面就匹配成功。可通过setAction方法为Intent设置action,也可在构造Intent时传入action。需要注意的是,隐式Intent必须指定action。比如我们在Manifest文件中为MyActivity定义了如下Intent Filter:
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.SEND_TO"/>
</intent-filter>
那么只要Intent的action为“SEND”或“SEND_TO”,那么这个Intent在action方面就能和上面那个Activity匹配成功。比如我们的Intent定义如下:
Intent intent =newIntent("android.intent.action.SEND") ;startActivity(intent);
那么我们的Intent在action方面就与MyActivity匹配了。
Android系统预定义了许多action,这些action代表了一些常见的操作。常见action如下(Intent类中的常量):
Intent.ACTION_VIEW
Intent.ACTION_DIAL
Intent.ACTION_SENDTO
Intent.ACTION_SEND
Intent.ACTION_WEB_SEARCH
总结起来有两点结论:
1. 要想让intent对象通过action测试,那么intent-filter中声明的action不能为空且要包含intent对象中的action值(如果intent的action值不为空的话)。
2. 如果intent-filter没有声明任何action,那么所有的intent的对象(即无论intent如何配置)都无法通过intent-filter的action测试。
如果Intent没有提供type,系统将从data中得到数据类型。和action一样,同action类似,只要Intent的data只要与Intent Filter中的任一个data声明完全相同,data方面就完全匹配成功。
data由两部分组成:mimeType和URI
MineType指的是媒体类型:例如imgage/jpeg,auto/mpeg4和viedo/*等,可以表示图片、文本、视频等不同的媒体格式
uri则由scheme、host、port、path | pathPattern | pathPrefix这4部分组成
其中scheme既可以是Android中常见的协议,也可以是我们自定义的协议。Android中常见的协议包括content协议、http协议、file协议等,自定义协议可以使用自定义的字符串。
如下是一个content协议的URI:
content://com.example.project:200/folder/subfolder/etc
在该URI中,scheme是content,host是com.example.project,port是200,path是folder/subfolder/etc。
如下是一个自定义协议的URI:
ispring://blog.csdn.net/sunqunsunqun
在该URI中,scheme是ispring,host是blog.csdn.net,没有明确设定port,path是sunqunsunqun。
组成URI的这些属性在标签中都是可选的 ,但存在如下的依赖关系:
如果没有指定scheme,那么host参数会被忽略
如果没有指定host,那么port参数会被忽略
如果scheme和host都没有指定,path参数会被忽略
当我们将intent对象中的Uri参数与intent-filter中的标签指定的URI格式进行对别时,我们我们只对比intent-filter的标签指定的部分,例如:
如果intent-filter中只指定了scheme,那么所有带有该sheme的URI都能匹配到该intent-filter。
如果intent-filter中只指定了scheme和authority(authority包括host和port两部分)而没有指定path,那么所有具有相同scheme和authority的URI都能匹配到该intent-filter,而不用考虑path为何值。
如果intent-filter中同时指定了scheme、authority和path,那么只有具有相同scheme、authority和path的URI才能匹配到该intent-filter。
需要注意的是,intent-filter的标签在指定path的值时,可以在里面使用通配符*,起到部分匹配的效果。
data测试需要同时将intent对象中的URI、MIME类型与intent-filter的标签中指定的URI、MIME类型进行对比。
我们知道一个intent-filter下可以有多个标签,intent对象无需通过所有的标签测试,一般情况下,我们的intent对象只需通过了其中一个标签的测试并满足某些特定情形下的一些条件,那么该intent对象就通过了该intent-filter的data测试。
Intent的uri可通过setData方法设置,mimetype可通过setType方法设置。
需要注意的是:若Intent Filter的data声明部分未指定uri,则缺省uri为content或file,Intent中的uri的scheme部分需为content或file才能匹配;若要为Intent指定完整的data,必须用setDataAndType方法,究其原因在,setData和setType方法的源码中我们发现:
publicIntentsetData(Uri data) {
mData = data;
mType =null;
returnthis;
}
publicIntent setType(Stringtype) {
mData =null;
mType =type;
returnthis;
}
这两个方法会彼此互相清除对方的值(这个比较逗),即setData会把mimeType置为null,setType会把uri置为null。
下面我们来举例说明一下data的匹配。首先我们先来看一下Intent Filter中指定data的语法:
<data android:scheme="String.“
android:host="String"
android:port="String"
android:path="String"
android:pathPattern="String"
android:pathPrefix="String"
android:mimeType="String"/>
其中scheme、host等各个部分无需全部指定。
使用案例:
(1)如果我们想要匹配 http 以 “.pdf” 结尾的路径,使得别的程序想要打开网络 pdf 时,用户能够可以选择我们的程序进行下载查看。
我们可以将 scheme 设置为 “http”,pathPattern 设置为 “.*//.pdf”,整个 intent-filter 设置为:
<intent-filter>
<actionandroid:name="android.intent.action.VIEW">
<categoryandroid:name="android.intent.category.DEFAULT">
<dataandroid:scheme="http"android:pathPattern=".*//.pdf">
</intent-filter>
如果你只想处理某个站点的 pdf,那么在 data 标签里增加 android:host=”yoursite.com” 则只会匹配 http://yoursite.com/xxx/xxx.pdf,但这不会匹配 www.yoursite.com,如果你也想匹配这个站点的话,你就需要再添加一个 data 标签,除了 android:host 改为 “www.yoursite.com” 其他都一样。
(2)如果我们做的是一个IM应用,或是其他类似于微博之类的应用,如何让别人通过 Intent 进行调用出现在选择框里呢?我们只用注册 android.intent.action.SEND 与 mimeType 为 “text/plain” 或 “/” 就可以了,整个 intent-filter 设置为:
<intent-filter>
<actionandroid:name="android.intent.action.SEND"/>
<categoryandroid:name="android.intent.category.DEFAULT"/>
<datamimeType="*/*"/>
</intent-filter>
这里设置 category 的原因是,创建的 Intent 的实例默认 category 就包含了 Intent.CATEGORY_DEFAULT ,google 这样做的原因是为了让这个 Intent 始终有一个 category。
(3)如果我们做的是一个音乐播放软件,当文件浏览器打开某音乐文件的时候,使我们的应用能够出现在选择框里?这类似于文件关联了,其实做起来跟上面一样,也很简单,我们只用注册 android.intent.action.VIEW 与 mimeType 为 “audio/*” 就可以了,整个 intent-filter 设置为:
<intent-filter>
<actionandroid:name="android.intent.action.VIEW"/>
<categoryandroid:name="android.intent.category.DEFAULT"/>
<dataandroid:mimeType="audio/*"/>
</intent-filter>
category也是一个字符串,但是它与action的过滤规则不同,它要求Intent中个如果含有category,那么所有的category都必须和过滤规则中的其中一个category相同。也就是说,Intent中如果出现了category,不管有几个category,对于每个category来说,它必须是过滤规则中的定义了的category。当然,Intent中也可以没有category(若Intent中未指定category,系统会自动为它带上“android.intent.category.DEFAULT”),如果没有,仍然可以匹配成功。category和action的区别在于,action要求Intent中必须有一个action且必须和过滤规则中的某几个action相同,而category要求Intent可以没有category,但是一旦发现存在category,不论你有多少,每个都要能够和过滤规则中的任何一个category相同。我们可以通过addCategory方法为Intent添加category。
1. 如果intent对象不包含任何category,并且该intent不是用来启动Activity的,那么该intent对象总是能通过所有任意的intent-filter的category测试;
2. 如果intent对象包含category(至少一个),那么只有当intent-filter中声明的category全部包含intent对象中的所有category的时候才通过category测试。
3. 如果允许Activity被隐式的Intent启动,那么我们必须在该Activity的intent-filter中声明值为android.intent.category.DEFAULT的category。
特别说明:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
这二者共同出现,标明该Activity是一个入口Activity,并且会出现在系统应用列表中,二者缺一不可。
(1)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”…
转义:因为当读取 Xml 的时候,“/” 是被当作转义字符的(当它被用作 pathPattern 转义之前),因此这里需要两次转义,读取 Xml 是一次,在 pathPattern 中使用又是一次。如:“” 这个字符就应该写成 “//”,“/” 这个字符就应该写成 “////”。
(2)查询是否有Activity可以匹配我们指定Intent的组件
采用PackageManager的resolveActivity或者Intent的resolveActivity方法会获得最适合Intent的一个Activity
调用PackageManager的queryIntentActivities会返回所有成功匹配Intent的Activity
(3)android.intent.action.MAIN 与android.intent.category.LAUNCHER的区别
区别一:
android.intent.action.MAIN决定一个应用程序最先启动那个组件
android.intent.category.LAUNCHER决定应用程序是否显示在程序列表里(说白了就是是否在桌面上显示一个图标)
这两个属性组合情况:
第一种情况:有MAIN,无LAUNCHER,程序列表中无图标
原因:android.intent.category.LAUNCHER决定应用程序是否显示在程序列表里
第二种情况:无MAIN,有LAUNCHER,程序列表中无图标
原因:android.intent.action.MAIN决定应用程序最先启动的Activity,如果没有Main,则不知启动哪个Activity,故也不会有图标出现
所以这两个属性一般成对出现。
如果一个应用中有两个组件intent-filter都添加了android.intent.action.MAIN和
android.intent.category.LAUNCHER这两个属性, 则这个应用将会显示两个图标, 写在前面的组件先运行。
区别二:
android.intent.category.LAUNCHER:android.intent.category.LAUNCHER决定应用程序是否显示在程序列表里,就是android开机后的主程序列表。
android.intent.category.HOME:按住“HOME”键,该程序显示在HOME列表里。
每一个通过 startActivity() 方法发出的隐式 Intent 都至少有一个 category,就是 “android.intent.category.DEFAULT”,所以只要是想接收一个隐式 Intent 的 Activity 都应该包括 “android.intent.category.DEFAULT” category,不然将导致 Intent 匹配失败.
首先查看Intent的过滤器(intent-filter),按照以下优先关系查找:action->data->category