浅析Android中Intent以及Intent Filter的运用

前述

Android中的三大核心组件:Activity,Service,Broacast Receive他们各自之间是通过Intent来进行彼此联系、触发的,Intent是种被动的数据结构,它启动三大组件的机制是:

Activity:Context.startActivity()Activity.startActivityForResult(),(Activity.setResult()可以通过Intent把信息传递给调用了startActivityForResult()方法的activity)。

Service:Context.startService()可以通过Intent初始化一个service或者传递指令给一个已经工作的service。相似的,Context.bindService()可以通过Intent在发起呼叫的组件和目标service之间建立连接。Intent可以任意的初始化还没有运行的service

Broacast Receive:Context.sendBroadcast(),Context.sendOrderedBroadcast()Context.sendStickyBroadcast(),可以通过Intent把消息传递给Broadcast Receiver。很多广播事件发起于系统级别的代码。

startActivity()只是把Intent传递给activity,而不是service或是broadcast receiver,以此类推。

Intent对象的组成

组件名---Component name

组件名字中所包含的包名的部分不需要必须与manifest文件中的包名相匹配,如:当package="info.kagoy.contentprovider",组件名可以是:x.xx.xxx.XActivity。

组件名字可以通过setComponent(),setClass()setClassName()来设置,通过getComponent()来读取。

如下:

                Intent intent = new Intent();
                ComponentName com = new ComponentName(
                        "Your package name(Set it in the AdroidManifest file)",
                        "Your Activity name(x.xx.xx.XActivity)");

                intent.setComponent(com);

                startActivity(intent);


eg:

                ComponentName cpName = new ComponentName(
                        "info.kagoy.contentprovider",
                        "info.kagoy.contentprovider.OtherActivity");
                Intent intent = new Intent();
                intent.setComponent(cpName);
                startActivity(intent);

   

    Log.d("huang", "MainActivity.this.getComponentName()="
                + MainActivity.this.getComponentName()
                + "||getIntent().getComponent()" + getIntent().getComponent());

打出的值是一样的。

动作---Action

一个命名了将要被执行的动作的字符串,或在广播intents事件中,已经发生并被报告的动作。Intent类定义了许多动作常量,可以通过查看 Intent类的定义查看一系列的代表一般行为动作的常量。通常我们可以自己定义动作的常量串,一个格式如:x.xx.xxx.XXX_XXX。我们用 setAction()来设置Intent中的action,并用 getAction()来读取。

如:

                Intent intent = new Intent(MainActivity.this,
                        OtherActivity.class);
                intent.setAction("info.kagoy.intent.OtherActivity");

                startActivity(intent);

在Intent的构造方法中没有指定参数,或者没有通过设置组件名方式指定的话,会出现 android.content.ActivityNotFoundException: No Activity found to handle Intent { act=haha cat=[android.intent.category.DEFAULT] }错误。出现此错误是,这是一种隐式的Intent格式,也就是说,你正在使用的Intent的隐式的,而非显示的,对于显示的Intent设不设置过滤器都一样,一样通过。你在使用过滤器的过程中在<intent-filter>中没有设置:<category android:name="android.intent.category.DEFAULT" />,因为在,startActivity(intent);//默认调用intent.addCategory("android.intent.category.DEFAULT"),所以在清单文件中必须加上。(与<intent-filter><action android:name="xxx" /></intent-filter>关系???当之是设置这句话时,使用getAction得到的是null。因为这里不是设置Action,而是一个过滤器中的一条门而已,需要在Java代码中设置一把钥匙,也就是setAction方法,这两个值必须一样,才会匹配。以上描述,其实是隐式的Intent,假如使用的是显示的Intent,无论在过滤器中设置了什么,一切都是浮云,一样执行。)

数据---Data

数据运行的URI和其MIME类型,当为某个组件匹配一个可以处理数据的Intent的时候,通常除了要了解Data的URI以外,重要的是要知道Data的类型(MIME type),例如,一个可以展示图片的组件不应该被调用来播放音频。Data的类型可以从URI中推测出来,特别是URI所展示的内容:指出了Data被用在什么位置及被哪种content provider控制(参考separate discussion on content providers)。但是Data的类型也可以在Intent中明确的设定。setData()方法设置Data的URI,setType()设置Data的类型(MIME type),setDataAndType()两者一起设置,getData()读取URI,getType()读取类型。

分类---Category

Category是这样一个String:他包含了需要处理Intent的组件的种类的信息,很多Category的描述能够放在Intent里,就像Action那样,Intent也定义了一些Category常量,Intent可以查看全部Category的列表。addCategory()方法可以把一个Category放入到Intent中,removeCategory()可以删除之前加入的Category,getCategories()可以得到目前在Intent中所有的Category。

Extras

Extras是传递给目标组件的键值对信息,Intent有一系列的put..()方法用以向Extras中插入各种类型的值,并且也有一系列的get..()方法来取出数值。可以采用,intent.putExtra和intent.getXXXExtra分别插入和读取;还有将数据用Bundle对象封装,如:

插入数据,

Bundle data = new Bundle();
data.putXXX(key, value);
                
intent.putExtras(data);

获取数据:

Bundle data  = intent.getExtras();

data.getXXX(key);

标志位---Flags

通知Android System如何运行一个activity(例如某个activity应该属于哪个任务)和运行以后如何处理(例如,flag是否属于当前活动activity)。所有这些flag都是在Intent中定义的。


Intent解析

Intent的两种形式

显示形式:

指定一个目标组件通过其name( Component name field), 由于组件名称通常不会被其它应用程序的开发者知道。所以,显示意图通常用在应用程序内部消息。如:一个Activity 启动一个从属的service或者启动另一个activity。

隐式形式:

不指定目标组件名称(component name 是空的)隐式意图通常用于去激活其它应用程序的组件。

Android 传递了一个显示意图给一个被指定的目标类的实例,被传递的 intent object 只是定义了component name ,它决定了将会有那个组件去处理这个intent。

针对隐式意图需要不同的策略。在缺乏一个被指定的target的情况下,android系统必须找到最适合的组件去处理这个intent ,一个单一的activity 或者 service 去执行一个请求动作或者一组broadcase receiver 去响应广播通知。

它通过将intent 对象中的内容 和 意图过滤器(intent filters)进行比较,android系统根据intent filter打开可以接收intent的组件,如果一个组件没有intent filter, 那么它只能接受显式intent,如果有, 则能同时接受二者。

当一个intent和intent过滤器进行比较时只会考虑以下三方面:

action 
data (both URI and data type) 
category

Intent过滤器---Intent filters

显性的intent总是被传递给指定的目标,无论过滤器里设置了什么条件。每个过滤器描述了组件的一个能力也限定一些组件可以接收的intent.实际上过滤器是筛进组件想要的intent,而不是筛出不需要的intent--仅仅是不需要的隐含的intent。

一个Intent过滤器就是IntentFilter类的一个实例。然而,由于Android System必须在加载组件之前知道这个组件的功能,所以Intent过滤器通常不在java代码里设置,而是在AndroidManifest.xml利用<intent-filter>来设置。(一个例外可能就是broadcast receivers的过滤器,他们是通过Context.registerRecerver()动态注册的,他们作为InterFilter对象被直接创建)。

一个Intent过滤器的安全性不可靠。当它打开一个组件准备接收确定类型的隐含intents时,它并不能阻止目标组件中的显性intents。即使一个过滤器阻止了一个组件被要求处理的确定动作和数据源,别人依然可以使用不同的动作和数据源绑定到一个显示的intent上,并将其命名为与目标组件相同的名字。

在Intent中filter与action,data,category是平行关系。一个implicit intent要想通过intent filter必须要在action,data,category三个方面通过校验。如果其中一个没有通过,Android System就不会把intent传递给组件。然而,由于一个组件拥有很多的intent过滤器,所以只要通过其中一个就可以了。

显式意图我们前面已经提到,形如:

Intent intent = new Intent();

intent.setClass(this,Other.class);//此句表示显式意图,因为明确设置激活对象为Other类,也即设置好了setComponent(),setClass()setClassName()

startActivity(intent);

在使用隐式的Intent,每个过滤器必须要在加上<category android:name="android.intent.category.DEFAULT" />。

1、Action

  

<intent-filter . . . >
    <action android:name="com.example.project.SHOW_CURRENT" />
    <action android:name="com.example.project.SHOW_RECENT" />
    <action android:name="com.example.project.SHOW_PENDING" />
    . . .
</intent-filter>
         

例子:

<intent-filter>
                <action android:name="haha" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>

当一个Intent对象只命名了一个动作,过滤器可能会列举多个,列表不能为空;一个过滤器至少需要包含一个<action>元素,否则它不会匹配任何intents。

为了通过这个测试,Intent对象中所指定的动作必须与过滤器中所列举的动作之一匹配。如果这个对象或过滤器没有指定一个动作,其结果如下:如果过滤器没有列举任何动作,那么就没有动作能够用来与intent相匹配,所以,所有的intents都不能通过测试,没有intents可以通过过滤器。

2、category

<intent-filter . . . >
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    . . .
</intent-filter>
因此,从理论上来说,不考虑过滤器中的值,一个没有分类的intent对象应该始终能通过这个测试。大部分情况下是对的。然而,有一个例外,Android把所有传递
给startActivity()的隐性intents都看作是至少有一个分类:"android.intent.category.DEFAULT"(即CATEGORY_DEFAULT常量)。所以,想要接收隐性
intent对象的活动必须在intent过滤器中包含"android.intent.category.DEFAULT"(过滤器中含有"android.intent.action.MAIN""android.intent.category.LAUNCHER"设定的为异常。它们标记了活动开始新任何,并显示在启动屏幕上。它们可以在分类列表中包括
"android.intent.category.DEFAULT",但不需要这么做。)

3、Data

如同动作与分类,intent过滤器中的数据指定也包含在了一个子元素当中。并且,在这种情况下,子元素可以多次出现,或不出现。例如:

<intent-filter . . . >
    <data android:mimeType="video/mpeg" android:scheme="http" . . . /> 
    <data android:mimeType="audio/mpeg" android:scheme="http" . . . />
    . . .
</intent-filter>

每一个<data>元素可以指定一个URI和一个数据类型(MIME媒体类型)。有多个属性——schemehostportport——组成了URI的每一个部分:

scheme://host:port/path

例如,在以下的URI中,content://com.example.project:200/folder/subfolder/etc

其中,scheme为"content",host为"com.example.project",port是"200",path为"folder/subfolder/etc"。host与port一起,组成了URI权限,

如果没有指定host,那么port也将被无视。

这些属性都是可选的,但它们之间并不相互独立:为了有意义的授权,scheme必须被指定。为了使路径(path)有意义,scheme和权限(authority)都必须被指定。

当Intent对象中的URI与过虑器中指定的URI对比时,只对比在过滤器中提及的部分。例如,如果过滤器只指定了数据类型,所有拥有这个scheme的URIs都能匹配。

如果过滤器指定了一个scheme和权限(authority),但没有路径(path),所有拥有相同scheme和权限(authority)的URIs将匹配,不考虑其路径(path)。

如果过滤器指定了scheme,权限(authority)和路径(path),那么,只有在URIs相同的这三个属性时才匹配。然而,过滤器中的路径指定可以包含通用符,

来要求路径的部分匹配。

<data>元素中的type属性指定了数据的MIME类型。在过滤器中,它比URI更常见。Intent对象和过滤器都可以使用"*"通用符作为子类型域——如,"text/*"

"audio/*"——指定任意一个匹配的子类型。

数据测试同时对比Intent对象中和过滤器中所指定的URI和数据类型。规则如下:

a.一个既不包含URI又没有指定数据类型的Intent对象只有在过滤器同样什么都没指定的情况下才能通过测试。

b.任意一个只包含了URI但没有数据类型(并且无法从URI中推断出其类型)只有在这个URI与过滤器中的一个URI相匹配,并且同样没有指定数据类型的情况下通过测

试。当URI为mailto:tel:时即为这种情况,并不能确切的知道数据类开。

c.Intent对象中包含了一个数据类型但没有URI时,只有在过滤器列举了相同的数据类型,并且类似有没有指定URI时通过测试。

d.Intent对象同时包含了一个URI和一个数据类型(如数据类型可以由URI推断出)只有在该类型与过滤器中所列类型匹配时才能通过测试。它将通过URI部分的测试,

要么URI与过滤器的中某个匹配,要么其包含了一个content:file:URI并且过滤器没有指定一个URI。换句话说,如果过滤器中听指定了数据类型,那么一个组件

默认的支持span style="color:green">content:</span>和file:数据。

如果一个intent可以通过多个活动和服务的过滤器,那么用户将需要选择激活哪个组件。如果找不到目标,刚会抛出一个异常。
eg 1:
  1. <data  
  2.                android:host="www.kagoy.com"  
  3.                android:scheme="kagoy"/>
  4. intent.setData(Uri.parse("kagoy://www.kagoy.com/xia"));

eg 2:

            android:host="www.kagoy.com"
            android:scheme="kagoy" android:mimeType="text/*"/>

intent.setDataAndType(Uri.parse("kagoy://www.kagoy.com/xia"), "text/plain");    //匹配了text/*

4、一般案例

 在上一个Data test提到的最后一条(d),表达出一种期望:组件可以从文件或content provider中获得数据。因此,他们的过滤器只需列出数据的类型而不需要给 
content: schemes和 
file: schemes明确命名。下面是一个经典的例子。一个 
<data>元素,告诉Android,组件可以从content provider获得图片数据并且显示: 

<data android:mimeType="image/*" />
因为大多数可用的数据是由content provider配与的,过滤器只指定数据类型而不指定URI可能成为最常见的。

另一种通用的配置是过滤器指定scheme和数据类型。例如,一个<data>元素,告诉Android,组件可以从网络获得视频数据并显示:

<data android:scheme="http" android:type="video/*" />

5、intent匹配

Intents与intent filters匹配,不仅仅是为了找到激活的目标组件,而且也为了找到关于设备上的组件的相关信息。例如,Android系统填充应用程序启动器,顶层屏幕显示了用户可以启动的应用程序,通过寻找所有在intent filters中指定了"android.intent.action.MAIN"动作和"android.intent.category.LAUNCHER"分类(如前文中所展示的)的活动。然后将这些活动的图标和标签显示在启动器中。相似地,通过寻找filter中的"android.intent.category.HOME"来发现主屏幕上的应用。


你的应用程序可以使用intent匹配,也是通过类似的方法。 PackageManager拥有一系列的 query...()方法,这些方法可以用来返回所有可以接收一个特定intent的组件,和相似的一系列 resolve...()方法用来决定最佳的组件来响应这个intent。例如, queryIntentActivityies()返回了一个所有能通过intent的活动的列表, queryIntentService()类似地返回了一个服务列表。但这两个方法都不激活组件;他们只是列表了所有可以响应的组件。对于广播接收器也有类似的方法, queryBroadcastReceivers



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值