Android-0.Intent及Intent Filter简介

文章目录

    • Intent的用途
    • Intent的类型
      • 显式的Intent
      • 隐式的Intent
      • Intent的组成
        • Component name(组件名)
        • Action(操作)
        • Data(数据)
        • Category (类别)
        • Extras
        • Flags (标志)
      • 隐式Intent使用示例
      • 强制用户使用应用选择器
    • Intent对象与Intent Filter过滤匹配过程
      • Action测试
        • intent对象设置了action
        • intent对象没有设置action
        • 总结
      • Category测试
        • intent对象至少有一个category
        • intent对象不包含任何category
        • 总结
      • Data测试
        • intent对象不包含URI和MIME类型
        • intent对象包含URI但不包含MIME类型
        • intent对象包含MIME类型但不包含URI
        • intent对象同时包含URI和MIME类型

官方文档: https://developer.android.google.cn/guide/components/intents-filters

Intent的用途

  1. startActivity()startActivityForResult()方法启动ActivityIntent对象包含了要启动的Activity的信息及其他必要的数据。
  2. startService()bindService()方法启动ServiceIntent对象包含了要启动的Service的信息及其他必要的数据。
  3. sendBroadcast()sendOrderedBroadcast()sendStickyBroadcast()方法发送广播。

Intent的类型

显式的Intent

如果Intent中明确包含了要启动的组件的完整类名(包名及类名),那么这个Intent就是显式的。

隐式的Intent

反之就是隐式的, 一般,隐式intent不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理它。
例如,你想在地图上给用户显示一个位置,但是你的App又不支持地图展示,这时候你可以将位置信息放入到一个Intent中,然后给它指定相应的action,通过这样隐式的Intent请求其他地图类型的App(例如Google Map、百度地图等)来在地图中展示一个指定的位置。

模块解藕示例:https://www.cnblogs.com/xjx22/p/5551034.html

当创建了一个隐式Intent时,Android系统会将该隐式Intent所包含的信息与设备上其他所有AppAndroidManifest.xml中的进行对比过滤,从中找出满足能够接收处理该隐式IntentApp和对应的组件。如果有多个App中的某个组件都符合条件,那么Android会弹出一个对话框让用户选择需要启动哪个App

其实同样可以理解为监听者模式, 而隐式Intent的调用就是一个广播查找调用。

Intent的组成

Android可以根据Intent所携带的信息去查找要启动的组件,启动的组件根据Intent携带的一些数据信息做相应的处理。
Intent由6部分信息组成:Component NameActionDataCategoryExtrasFlags

  1. Component NameActionDataCategory、决定了Android会启动哪个组件,其中Component Name用于显式IntentIntentActionDataCategoryExtrasFlags用于在隐式Intent
  2. Extras里面包含了具体的用于组件实际处理的数据信息。
  3. FlagsIntent的元数据,决定了Android对其操作的一些行为。

Component name(组件名)

要启动的组件名称。如果你想使用显式的Intent,那么你就必须指定该参数,一旦设置了component name,Android会直接将Intent传递给组件名所指定的组件去启动它。

需要注意的是,当启动Service的时候,你应该总是指定Component Name。否则,你不确定最终哪个App的哪个组件被启动了,并且用户也看不到哪个Service启动了。

component nameIntent中对应的fieldComponentName对象,你可以通过要启动的组件的完整类名(包括应用的包名)指定该值,例如com.example.ExampleActivity。你可以通过IntentsetComponent()方法、setClass()方法、setClassName()方法或Intent的构造函数指定component name

Action(操作)

表示了要执行操作的字符串。比如查看选择,其对应着中的标签.

预定义的具有通用意义的action

  1. Intent.ACTION_VIEW 值为 “android.intent.action.VIEW”,当你有一些信息想通过其他Activity展示给用户的时候,你就可以将Intentaction指定为ACTION_VIEW,比如在一个图片应用中查看一张图片,或者在一个地图应用中展现一个位置。
  2. Intent.ACTION_SEND 值为”android.intent.action.SEND”,该action常用来做“分享”使用,当你有一些数据想通过其他的App(例如QQ、微信、百度云等)分享出去的时候,就可以使用此action构建Intent对象,并将其传递给startActivity()方法,由于手机上可能有多个App的Activity均支持ACTION_SEND这一action,所以可能出现多个APP供你选择。

自定义action
请确保将应用的软件包名称作为前缀,例如:

你可以指定你独有的action以便于你的App中的Intent使用或其他App中通过Intent调用你的App中的组件。

你可以通过调用intentsetAction()方法或在Intent的构造函数中指定intentaction

Data(数据)

此处所说的Intent中的data指的是Uri对象和数据的MIME类型,对应着中的标签。

一个完整的Uri由schemehostportpath组成,格式是://:/。例如content://com.example.project:200/folder/subfolder/etc

Uri就像一个数据链接,组件可以根据此Uri获得最终的数据来源。通常将Uri和action结合使用,比如我们将action设置为ACTION_VIEW,我们应该提供将要被编辑修改的文档的Uri。

当创建了一个Intent对象的时候,除了指定Uri之外,指定数据的MIME类型也很重要。

例如,一个Activity能够显示图片,但是不能够播放视频,显示图片的Uri和播放视频的Uri可能很类似,为了不让Android误将一个含有视频Uri的Intent对象传递给一个只能显示图片的Activity,我们需要在该Activity中指定MIME类型为图片(例如)并且还要给Intent对象设置对应的图片类型的MIME,这样Android就会基于Uri和MIME类型将Intent传递给符合条件的组件。

有个特例,如果Uri使用的是content:协议,那么这就说明Uri所提供的数据将来自于本地设备,即数据由ContentProvider提供,这种情况下Android会根据Uri自动推断出MIME类型,此种情况我们无需再自己指定MIME类型。

需要注意的是,如果你想要同时设置数据的Uri和MIME类型,不要先后调用Intent对象的setData()方法和setType()方法,因为setData()方法和setType()是互斥的,即如果调用了setData()方法,会将Intent中已经通过setType()方法设置的MIME类型重置为空。如果调用了setType()方法,会将Intent中已经通过setData()方法设置的Uri重置为空。所以在需要同时设置数据的Uri和MIME类型的时候,一定要调用Intent对象的setDataAndType()方法,而不是分别调用setData()方法和setType()方法。

Category (类别)

category包含了关于组件如何处理Intent的一些其他信息,虽然可以在Intent中加入任意数量的category,但是大多数的Intent其实不需要category

以下是一些常见的category

  1. CATEGORY_BROWSABLE 目标 Activity 允许本身通过网络浏览器启动,以显示链接引用的数据,如图像或电子邮件。
  2. CATEGORY_LAUNCHER 用于标识Activity是某个App的入口Activity

Extras

extras,顾名思义,就是额外的数据信息,Intent中有一个Bundle对象存储着各种键值对,接收该Intent的组件可以从中读取出所需要的信息以便完成相应的工作。有的Intent需要靠Uri携带数据,有的Intent是靠extras携带数据信息。

你可以通过调用Intent对象的各种重载的putExtra(key, value)方法向Intent中加入各种键值对形式的额外数据。你也可以直接创建一个Bundle对象,向该Bundle对象传入很多键值对,然后通过调用Intent对象的putExtras(Bundle)方法将其设置给Intent对象中去。

例如,你创建了一个actionACTION_SEND用于发送电子邮件的 Intent 时,
Intent.EXTRA_EMAIL 指定“目标”收件人,用Intent.EXTRA_SUBJECT 指定“主题”。

Intent 类将为标准化的数据类型指定多个 EXTRA_* 常量。如需声明自己的 extra 键(对于应用接收的 Intent),请确保将应用的软件包名称作为前缀。 例如::static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";

Flags (标志)

Intent 类中定义的、充当 Intent 元数据的标志。 标志可以指示 Android 系统如何启动 Activity(例如,Activity 应属于哪个任务),以及启动之后如何处理(例如,它是否属于最近的 Activity 列表)。。更多信息可参见IntentsetFlags()方法。

隐式Intent使用示例

例如,你有一段文本信息,想通过其他App分享出去,示例代码如下:

Intent sendIntent = new Intent();
// 设置action, action对隐式Intent来说是非常重要的
sendIntent.setAction(Intent.ACTION_SEND);
// 设置数据的MIME类型为纯文本类型
sendIntent.setType("text/plain");
// 设置额外的数据
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);

// 获取包管理器
PackageManager pm = getPackageManager();
// 先判断系统中有没有潜在的App的Activity支持对该sendIntent的接收与处理
if (pm.resolveActivity(sendIntent, 0) != null) {
    startActivity(sendIntent);
}

上面的代码中,我们构建了一个隐式的Intent对象。我们设置了action的值为Intent.ACTION_SENDaction对隐式Intent来说是非常重要的。然后我们将intent的数据的MIME类型设置为纯文本类型(“text/plain”),告知Android我们的Intent持有的是文本类型的数据。最后我们将实际的文本数据通过putExtra()方法作为额外数据设置进去。

需要注意的是,在构建好了Intent对象之后,我们没有立即执行startActivity(sendIntent)方法,而是将sendIntent作为参数传递给了PackageManagerresolveActivity()方法中,该方法会让Android根据该sendIntent找到潜在的适合启动的组件的信息,并以ResolveInfo类的对象的形式返回结果,如果返回null,表示当前系统中没有任何组件可以接收并处理该sendIntent。如果返回不是null,就表明系统中至少存在一个组件可以接收并处理该sendIntent,只有在这种情况下,我们才会执行代码startActivity(sendIntent),在通过intent启动组件之前先判断要启动的组件存不存在是个良好的编程习惯,因为如果系统中不存在支持你的intent的组件,那么当你调用startActivity()startService()bindService()等方法的时候,Android就会抛出异常。

强制用户使用应用选择器

如果有多个应用响应隐式 Intent,则用户可以选择要使用的应用,并将其设置为该操作的默认选项。 如果用户可能希望今后一直使用相同的应用执行某项操作(例如,打开网页时,用户往往倾向于仅使用一种网络浏览器),则这一点十分有用。

但是如果用户不想每次都用同一个默认App处理这样的情形怎么办呢?这时候我们可以在代码中明确地使用App选择对话框,比如我们的App执行一个action为ACTION_SEND的分享功能时,我们想让用户分享自己数据的代码,但是我们不确定用户想通过哪个App去分享,我们想每次都弹出App选择对话框让用户决定想通过哪个App分享,示例代码如下所示:

Intent sendIntent = new Intent(Intent.ACTION_SEND);
...

String title = "请选择想通过哪个App分享数据";

// 验证是否有App能够接收并处理sendIntent
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    // 根据sendIntent创建一个需要显示App选择对话框的intent对象
    Intent chooserIntent = Intent.createChooser(sendIntent, title);
    // 我们使用chooserIntent作为startActivity()方法的参数,而非sendIntent
    startActivity(chooserIntent);
}

首先我们创建了我们原始的sendIntent,并对其设置action等相关信息,然后我们将sendIntent传递给了Intent.createChooser()方法中,创建了另一个chooserIntent。后面我们通过调用Intent.resolveActivity(PackageManager)方法判断系统中是否有App能够接收并处理sendIntent,该方法与上面之前提到过的PackageManagerresolveActivity()方法是等价的。最后我们使用chooserIntent作为startActivity()方法的参数,而非sendIntentchooserIntent会让Android系统强制显示用户选择App处理Intent的界面。

Intent对象与Intent Filter过滤匹配过程

当Android系统接收到一个隐式Intent要启动一个Activity(或其他组件)时,Android会根据以下三个信息比较Intent的信息与注册的组件的的信息,从而为该Intent选择出最匹配的Activity(或其他组件):

  1. intent中的action
  2. intent中的category
  3. intent中的data(包含Uri以及data的MIME类型)

如果隐式intent对象同时通过了某个组件的中action测试、category测试以及data测试,那么该组件就可以被intent对象所启动。反之无法启动。下面我们依次看一下如何才能通过这三个测试。

Action测试

为了指定能够接收并处理的Intent的类型,组件可以在中声明其支持0个或多个action,例如:


    
    
    

intent对象可以通过setAction()方法设置唯一的一个action值。对于action测试,需要分两种情况:

intent对象设置了action

如果intent对象通过调用setAction()方法设置了action的值,那么只有当组件的中包含了intent对象中的action值的时候,action测试才通过,否则无法通过。

举个例子,假设我们的如下所示:


   
   
   
   

1.下面的intent对象可以通过上面里面的action测试:

Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST1");
Uri uri = Uri.parse("ispring://blog.csdn.net/sunqunsunqun");
intent.setData(uri);

intent之所以能通过action测试是因为中包含该intentactioncom.ispring.action.ACTION_TEST1

2.下面的intent对象无法通过上面里面的action测试:

Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST3");
Uri uri = Uri.parse("ispring://blog.csdn.net/sunqunsunqun");
intent.setData(uri);

intent之所以无法通过action测试是因为中不包含该intentactioncom.ispring.action.ACTION_TEST3

intent对象没有设置action

如果intent对象没有调用setAction()方法设置action的值,那么如果至少有一个任意的action的值,该intent对象就可以通过该的action测试,反之,如果中没有定义任何的action,那么该intent无法通过该action测试。

举个例子,假设我们的intent对象如下所示:

Intent intent = new Intent();
//不设置action值
//intent.setAction("com.ispring.action.ACTION_TEST1");
Uri uri = Uri.parse("ispring://blog.csdn.net/sunqunsunqun");
intent.setData(uri);

1.上面的intent对象可以通过如下的


    
    
    

2.上面的intent对象无法通过如下的


    
    

总结

总结起来有两点结论:

  1. 要想让intent对象通过action测试,那么中声明的action不能为空且要包含intent对象中的action值(如果intentaction值不为空的话)。
  2. 如果没有声明任何action,那么所有的intent的对象(即无论intent如何配置)都无法通过action测试。

Category测试

为了指定能够接收并处理的Intent的类型,组件可以在中声明其支持0个或多个category,例如:


    
    
    ...

intent对象有addCategory()方法,也就是说一个intent对象也可以关联多个category。为了能让intent对象通过category测试,intent对象中的所有category都要在中找到对应项。

intent对象至少有一个category

这种情况下,假设intent对象有N个category(N >=1),那么中必须要包含这N个categoryintent对象才能通过category测试,否则无法通过测试。如果用intent对象启动Activity,还有其他限制条件,会在后面详细说明。

举个例子,假设我们的如下所示:


    
    
    
    

1.以下intent对象能够通过category测试

Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST1");
intent.addCategory("com.ispring.category.TEST1");
intent.addCategory("com.ispring.category.TEST2");

intent对象之所以可以通过category测试是因为包含了该intent中所有的category值:com.ispring.category.TEST1com.ispring.category.TEST2

2.以下intent对象无法通过category测试

 Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST1");
intent.addCategory("com.ispring.category.TEST1");
intent.addCategory("com.ispring.category.TEST3");

intent之所以无法通过上面的category测试是因为只包含了该intent中值为com.ispring.category.TEST1category,而并未包含值为com.ispring.category.TEST3category

intent对象不包含任何category

如果intent对象没有调用过addCategory()方法,那么intent对象就不包含任何的category。这种情形下,如果该intent不是用来启动Activity的话,那么无论category中如何配置,intent对象总是能通过category测试,即便中没有声明任何的categoryintent都能通过category测试。此处强调了该intent不是用来启动Activity这种条件,会在下面详细解释。

此处需要特别说明的是,我们在上面所有的示例中,都给Activity添加了值为android.intent.category.DEFAULTcategory,这是因为当我们把一个隐式的intent传递给startActivity()startActivityForResult()方法时,Android会自动给该隐式intent添加值为android.intent.category.DEFAULTcategory,所以为了能让包含intent中全部的category,我们就需要在Activity的中添加该category,在使用时需要特别注意。

总结

根据上面我们的几个示例,我们总结如下:

  1. 如果intent对象不包含任何category,并且该intent不是用来启动Activity的,那么该intent对象总是能通过所有任意的category测试;
  2. 如果intent对象包含category(至少一个),那么只有当中声明的category全部包含intent对象中的所有category的时候才通过category测试。
  3. 如果允许Activity被隐式的Intent启动,那么我们必须在该Activity中声明值为android.intent.category.DEFAULTcategory

Data测试

为了指定可以接收的Intentdata需要声明0个或多个标签,例如:


    
    
    ...

每个标签都可以指定一个URI结构以及data的MIME类型。一个完整的URI由schemehostportpath组成,其结构如下所示:://:/
其中scheme既可以是Android中常见的协议,也可以是我们自定义的协议。Android中常见的协议包括content协议http协议file协议等,自定义协议可以使用自定义的字符串。

如下是一个content协议的URI:

    content://com.example.project:200/folder/subfolder/etc 

在该URI中,schemecontenthostcom.example.projectport200pathfolder/subfolder/etc

如下是一个自定义协议的URI:

ispring://blog.csdn.net/hgy 

在该URI中,schemeispringhostblog.csdn.net,没有明确设定portpathhgy

组成URI的这些属性在标签中都是可选的 ,但存在如下的依赖关系:

  1. 如果没有指定scheme,那么host参数会被忽略
  2. 如果没有指定host,那么port参数会被忽略
  3. 如果schemehost都没有指定,path参数会被忽略

当我们将intent对象中的Uri参数与中的标签指定的URI格式进行对别时,我们我们只对比标签指定的部分,例如:

  1. 如果中只指定了scheme,那么所有带有该sheme的URI都能匹配到该
  2. 如果中只指定了schemeauthorityauthority包括hostport两部分)而没有指定path,那么所有具有相同schemeauthority的URI都能匹配到该,而不用考虑path为何值。
  3. 如果中同时指定了schemeauthoritypath,那么只有具有相同schemeauthoritypath的URI才能匹配到该

需要注意的是,标签在指定path的值时,可以在里面使用通配符*,起到部分匹配的效果。

data测试需要同时将intent对象中的URI、MIME类型与标签中指定的URI、MIME类型进行对比。

我们知道一个下可以有多个标签,intent对象无需通过所有的标签测试,一般情况下,我们的intent对象只需通过了其中一个标签的测试并满足某些特定情形下的一些条件,那么该intent对象就通过了该data测试。

进行对比的规则分以下几种情况:

intent对象不包含URI和MIME类型

这种情况下,只有当也没有指定任何URI和MIME类型的时候才能通过data测试。

例如我们有如下intent对象:

Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST1");

1.上面的intent对象可以通过下面的的data测试:


    
    

2.上面的intent对象无法通过下面的测试:


    
    
    

intent对象包含URI但不包含MIME类型

这种情况下,只有当intent对象的URI匹配到了中的URI格式,并且没有指定MIME类型的时候才能通过data测试。需要注意的是,这里所说的没有指定MIME类型的情形指的是中所有的标签都没有指定MIME类型,即整个中完全没有android:mimeType这几个字,理解这点很重要,大家在下面的几个示例中可以体会到这点。

例如有如下intent对象:

Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST1");
Uri uri = Uri.parse("ispring://blog.csdn.net/hgy413");
intent.setData(uri);

1.上面的intent能通过如下的的data测试:


    
    
    

2.上面的intent对象可以通过以下的data测试:


    
    
    
    

intent对象虽然不能通过scheme为hgy的标签测试,但是可以通过scheme为ispring的data标签测试,且intent对象和中的两个标签都没有指定MIME,所以上面的intent对象可以通过该测试。

3.上面的intent对象无法通过以下标签测试:


    
    
    

上面的intent对象之所以不能通过中唯一的一个标签测试是因为我们的intent对象没有指定MIME类型,但是上面的标签通过android:mimeType="text/plain"设置了MIME类型。

4.上面的intent对象无法通过以下的data测试:


    
    
    
    

上面的intent对象之所以无法通过该中的data测试,是因为intent对象没有设置MIME类型,但是中第二个data标签通过android:mimeType="text/plain"设置了MIME类型。

intent对象包含MIME类型但不包含URI

这种情况下,只有当intent中的MIME类型与中列出的MIME类型相同,并且没有指定任何的URI格式的时候才能通过data测试。需要注意的是,这里所说的没有指定任何的URI格式的情形指的是中所有标签都没有指定URI,即整个中完全没有android:schemeandroid:hostandroid:port以及android:path,理解这点很重要,大家在下面的几个示例中可以体会到这点。

例如有如下intent对象:

Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST1");
intent.setType("text/plain");

1.上面的intent对象可以通过以下的data测试:


    
    
    

2.上面的intent对象可以通过以下的data测试:


    
    
    
    

上面的intent对象虽然没有通过MIME类型为image/*的第一个data标签测试,但能通过第二个data标签测试,并且intent对象和都没有指定任何的URI格式。

3.上面的intent对象不能通过以下中的data测试:


    
    
    

上面的intent对象中没有设置URI信息,但是在该中设置了URI中的scheme值,所以intent无法通过data测试。

4.上面的intent对象无法通过以下中的data测试:


    
    
    
    

上面的intent对象没有指定URI信息,但是上面的中第二个标签设置了URI中的scheme信息,所以intent对象无法通过该的data测试。

intent对象同时包含URI和MIME类型

这种情况下,要分别测试URI以及MIME类型测试是否通过,只有URI以及MIME测试都通过了,data测试才能通过。

  1. 对于MIME测试:如果intent的MIME类型能够匹配中列出的某一个标签中的MIME类型值,那么MIME类型测试就通过了。
  2. 对于URI测试:
    又细分两种情况,满足下面的任何一种情况都可以通过URI测试。
    (1).如果intent的URI格式能够匹配中列出的某一个中的URI,那么URI测试就通过了。
    (2). 如果intent的URI是content:协议或file:协议,并且整个的所有标签中都没有指定URI,那么该intent也能通过URI测试。换句话说,如果一个只列出了MIME类型,没有列出任何URI相关的格式的话,那么这个就默认是支持content:协议或file:协议的。

下面举几个例子大家自己体会一下。
假设有如下协议为自定义协议ispring:intent对象:

Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST1");
Uri uri = Uri.parse("ispring://blog.csdn.net/hgy");
String type = "text/plain";
intent.setDataAndType(uri, type);

1.上面的intent对象可以通过下面的data测试:


      
      
      
      

2.上面的intent对象无法通过下面的data测试:


      
      
      
      

port不满足,URI测试不通过,导致data测试失败。

3.上面的intent对象无法通过下面的的data测试:


      
      
      
      

android:mimeType不满足,MIME类型测试不通过,导致data测试失败。

假设有如下协议为content:intent对象:

Intent intent = new Intent();
intent.setAction("com.ispring.action.ACTION_TEST1");
Uri uri = Uri.parse("content://com.ispring.test");
String type = "text/plain";
intent.setDataAndType(uri, type);

1.上面的intent对象无法通过下面的的data测试:


      
      
      
      

URI中的scheme不匹配,导致URI测试不通过,导致data测试失败。

2.上面的intent对象可以通过下面的的data测试:


      
      
      

intent中使用的是content:协议,并且整个中都没有定义URI格式,所以URI测试是可以通过的,并且MIME类型能找到匹配项,所以可以通过data测试。

参考:
https://developer.android.google.cn/guide/components/intents-filters
https://blog.csdn.net/iispring/article/details/48481793

你可能感兴趣的:(Android)