Intent和Intent Filters

Intent用来向另一个app组件请求某种动作Action,它主要有三种用法:

1.开启Activity:

通过startActivity()或者startActivityForResult(),返回的结果可以在Activity的onActivityResult()回调函数中的Intent对象中获得;

2.开启一个Service:

通过startService()或者bindService();

3.发送广播:

所有的app都能接收到广播.系统本身就会发出很多的广播来表示系统事件的发生.可以通过sendBroadcast(),sendOrderedBroadcast()或者sendStickyBroadcast();


Intent类型:

1.显式Intent:

用类的完整名来指定所要启动的组件;

2.隐式Intent:

仅仅声明一个通用的动作,其他app的组件也可以被启动.

使用显示Intent时,系统立即启动相应的组件;使用隐式Intent时,系统先找到设备上所有其他app的manifest文件,然后将该Intent同manifest文件中的Intent Filter进行比较,如果相符合则直接启动该组件,如果有多个组件都符合,系统弹出一个对话框供用户选择.

Intent Filter是mannifest文件中用来指明某一组件希望接收Intent的类型表达式.如果声明了Intent Filter,其他用户就可以通过Intent来启动该组件;如果没有声明Intent Filter,则只能通过显式Intent来调用.

注意:

为了保证app是安全的,务必使用显式Intent来启动Service,不要给Service添加Intent Filter.这主要是因为Service是不可见的.


创建一个Intent:

Intent对象需要携带一个组件启动相关的所有信息.Intent主要包括以下几个内容:

1.组件名字:

这是可选的.有组件名字则是一个显式Intent,否则是隐式Intent(通过Intent中的其他数据确定所要启动的组件).

注意:

在启动一个Service时,应该始终指定组件名字,否则你将不知道Service被启动.

Intent对应的数据域是一个ComponetName对象,可以通过使用全限定类名来指定.使用setComponet(),setClass(),setClassName()或者构造函数来指定组件名字.

2.动作Action

指定通用动作的一个字符串.

在广播的Intent中,Action就是所发生的动作.动作很大程度上决定了Intent其他成员的构造过程,尤其是data和extras.


可以自定义Action,但是最好使用Intent类中或者其他framework类中的常数.下面是两个常见的启动Activity的action:

ACTION_VIEW:

当需要通过Activity显示一些信息时.可以在startActivity()中的Intent中指定该action;

ACTION_SEND:

也叫做share Intent,当有用户可以共享至其他app(例如邮件app或者社交app)的数据时,将Intent的action指定为ACTION_SEND.

可以在Intent的类说明中看到更多的action.其余action在其他地方定义,例如Settings类.

可以通过setAction()或者构造函数指定action.

在自定义action时,务必把前缀设置为自己的包名.


3.数据Data:

action对应数据的URI或者是该数据的MIME类型.data的类型通常在action中就可以知道.例如ACTION_EDIT时,data应该包含待编辑文档的URI.

在创建Intent时,除了数据的URI之外,通常数据的(MIME)类型也很重要.指定数据的MIME类型有助于安卓系统找到接收该Intent的最佳组件.有时候,MIME类型可以通过数据的URI推断出来,尤其是当数据是一个content:URI时(即数据在设备上,并且被ContentProvider所控制),此时系统知道该数据的MIME系统.

仅仅设置data URI时,使用setData();仅仅设置MIME类型时,使用setType();如果需要同时设置时,使用setDataAndType().

注意:

同时设置时不要分开调用setData()和setType(),因为他们都会相互清空另一个的值.使用setDataAndType().


4.Category:

一个包含Intent目标组件额外信息的字符串.虽然大多数的Intent不需要指定category,但是一个Inteng可以包含多个category描述.下面是一些实例:

CATEGORY_BROWSABLE:

目标 Activity允许自己被一个web浏览器启动,从而显示某一个链接上的数据.

CATEGORY_LAUNCHER

该Actiity是一个任务task的初始activity,且被放置在系统应用启动列表中.

查看Intent类.可以获得更多的category细节.

通过addCategory()可以添加category.


上述的这些设置属性定义了Intent的很多特性,安卓系统通过这些属性就可以知道所要启动的app组件.同时,Intent还可以携带与待启动组件的识别无关的附加信息.

1.Extras:

键值对,携带了实现相应action所需的附加信息.同一些action需要特定的数据URI一样,一些action需要使用特定的extras.

通过putExtra()方法可以多次添加键值对,也可以创建与i个Bundle对象,然后通过putExtras()方法一次性添加进Intent中.

例如,通过ACTION_SEND的Intent来启动发送email的组件时,可以将收件人设置为EXTRA_EMAIL键,附件设置为EXTRA_SUBJECT键.

Intent类指定了许多EXTRA_开头的常数来标准化数据类型,因此在自定义键时,最好加上自己app的包名作为前缀.

2.Flags:

Intent内的元数据.flag可以指定系统如何启动一个activity(例如该activity属于哪个任务task)以及如何对待被启动的activity(例如,该activity是否属于最近启动activity列表中).

具体的可以查看setFlags()方法.


显式Intent的例子:

// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);

隐式Intent的例子:

// Create the text message with a string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType(HTTP.PLAIN_TEXT_TYPE); // "text/plain" MIME type

// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(sendIntent);
}
注意:

当用户没有任何app可以处理该隐式Intent时,调用startActivity()会导致程序崩溃.因此可以通过resolverActivity()方法来确定是否有相应的app可以处理该Intent.


强制弹出app选择对话框:

当有多个app满足Intent时,用户可以选择一个app作为默认的app,但是有时候,用户每次都需要选择不同的app,例如在分享界面中.此时需要强制弹出app选择对话框.

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

// Always use string resources for UI text.
// This says something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show chooser
Intent chooser = Intent.createChooser(intent, title);

// Verify the intent will resolve to at least one activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}
注意:

官方文档可能有勘误,startActivity应该是chooser,而不是sendIntent;


接收一个隐式Intent

通过manifest文件中的<intent-filter>标签来指定.

注意:

一个显式的Intent将会忽略所有的intent filters声明,而直接启动它的目标组件.

每一个app组件应该对它所支持的所有任务都声明独立的intent filter.

<intent-filter>声明在组件内部,通过定义<action>,<data>或者<category>来指定相应的Intent属性.

注意:

在category中必须包含CATEGORY_DEFAULT,这是因为startActivity()和startActivityForResult()都默认它的Intent参数声明了CATEGORY_DEFAULT.如果没有声明CATEGORY_DEFAULT,则没有隐式Intent可以满足该activity的条件.(what about service and other componets?)

例如:

<pre name="code" class="html"><activity android:name="ShareActivity">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

 允许定义多个<action>,<data>和<category>.这意味着该组件可以处理所有filter元素组合对应的Intent. 

当只能处理一些特定的组合模式时,应该考虑创建多个intent filter.

注意:

所有的activity都应该声明intent filter;

broadcast receiver的filter可以通过registerReceiver()动态注册.也可以通过unregisterReceiver()解除注册.

filter示例:

<activity android:name="MainActivity">
    <!-- This activity is the main entry, should appear in app launcher -->
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity android:name="ShareActivity">
    <!-- This activity handles "SEND" actions with text data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
    <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <action android:name="android.intent.action.SEND_MULTIPLE"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="application/vnd.google.panorama360+jpg"/>
        <data android:mimeType="image/*"/>
        <data android:mimeType="video/*"/>
    </intent-filter>
</activity>
ACTION_MAIN说明这是主程序进入点,并且不需要intent中的data;

CATEGORY_LAUNCHER说明这个activity的图标将会放在系统的app启动界面上,如果<acttivity>没有指定一个icon,那么系统将会使用<application>指定的icon.
为了使得activity出现在app启动列表中,这两个属性需要成对使用.


使用PendingIntent:

这是Intent对象的包裹类.PendingIntent最初的设计目的是给其他app提供权限,使得该组件就像在自己app的进程中执行一样.

pending intent主要的用例如下:

1.声明一个intent供Notification使用(系统NotificationManager执行Intent);

2.声明一个intent供App Widget使用(主屏幕app执行Intent);

3.声明一个intent供将来某一特定时刻执行(系统的AlarmManager执行).

每一个Intent都是被一个特定的组件类型来处理的,PendingIntent也是一样.在使用PendingIntent时,就不要在app中使用startActivity()来执行该Intent.因此在创建PendingIntent对象时就需要指定其对应的组件类型:

1.PendingIntent.getActivity()用来创建一个启动Activity的Intent;

2.PendingIntent.getService()用来创建一个启动Service的Intent;

3.PendingIntent.getBroadcast()用来创建一个启动BroadcastReceiver的Intent.

除非你的app允许接收其他app的pending intent,否则仅仅需要上述用来创建PendingIntent对象的方法即可.

每个方法都接受contex,待包裹intent以及一个或者多个flag作为参数.

更多的信息可以查看相关文档,如Notification和App Widgets的API.


Intent解析:

当系统接收到隐式Intent请求时,会将intent同所有的intent filter进行比较,比较将包括三个方面:

1.action

2.data;

3.category


1.Action测试:

可以不指定或者指定多个<action>元素,例如:

<intent-filter>
    <action android:name="android.intent.action.EDIT" />
    <action android:name="android.intent.action.VIEW" />
    ...
</intent-filter>
Intent必须要满足一条action filter才能通过测试.

如果filter为空,则没有intent能够通过测试(找不到任意一条可以满足).当intent没有指定action时,只要此时filter也非空,则可以通过action测试.


2.category测试

可以不指定或者指定多个<category>,例如:

<intent-filter>
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    ...
</intent-filter>
Intent中的每一个category都需要同filter中的一项匹配.相反的,并不一定成立.如果一个intent没有指定category,则不论filter中定义了什么,都应该通过category测试(那么当filter也为空时呢?).

注意:

在调用startActivity()和startActivityForResult()时,系统会自动给Intent添加CATEGORY_DEFAULT,所以只有添加了相应filter的activity才能够被隐式Intent启动.


3.data测试

可以不指定或者指定多个<data>,例如:

<intent-filter>
    <data android:mimeType="video/mpeg" android:scheme="http" ... />
    <data android:mimeType="audio/mpeg" android:scheme="http" ... />
    ...
</intent-filter>
每个<data>元素可以指定一个URI结构或者一个data类型(MIME媒体类型).URI的没部分都有对应的设置属性:

<scheme>://<host>:<port>/<path>

例如:

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

并不需要指定所有的属性,但是这些属性间存在线性依赖,即:

1.如果scheme没有指定,则host被忽略;

2.如果host没有指定,则port被忽略;

3.如果scheme和host都没有指定,则pat被忽略.

filter中有什么属性,则Intent就将相应的属性同filter进行比较.例如:

1.如果一个filter仅仅指定了一个scheme,则所有含有该scheme的URI都与该filter匹配;

2.如果一个filter指定一个scheme和一个authority但是没有指定path,则不管path值是多少,只要scheme和authority与之匹配则通过测试;

3.如果一个filter指定一个scheme,一个authority和一个path,则必须都匹配才能通过测试.

data测试不仅会比较URI类型,也会同时比较MIME类型,对应的规则如下:

a.只有在filter不指定任何URI或者MIME类型时,既不包含URI也不包含MIME类型的Intent才会通过测试;

b.只有在filter指定URI,并且不指定MIME类型时,则仅仅包含URI而不包含MIME类型(不管是显式还是从URI推断出来)的Intent才能通过测试;

c.只有在filter指定MIME类型却不指定URI类型时,则仅仅包含MIME类型而不包含URI类型的Intent才能通过测试;

d.当Intent既包含URI也包含MIME类型时(不管是显式的还是从URI推断出来的),则只有在该类型与filer匹配时才能通过测试.当filter只指定MIME类型时,则Intent默认为content:或者file:.

在规则d中,系统希望该组件能够从file或者content provider中获得本地数据.因此,它们的filter只需要data类型而不需要显式地指定content:或者file:schemes.下面是一个典型用例.该<data>元素告诉安卓系统该组件能够从content provider中获得图片数据并且显示它:

<intent-filter>
    <data android:mimeType="image/*" />
    ...
</intent-filter>
因为大多数数据都是通过content provider分配的,filter仅仅指定数据类型却不指定URI的情况是最常见的.

另一个常见的设置是filter包含一个scheme和一个数据类型.如下所示的<data>元素告诉安卓系统目标组件应该从网络获取视频数据来完成这一次action:

<intent-filter>
    <data android:scheme="http" android:type="video/*" />
    ...
</intent-filter>

Intent匹配:

Intent同intent filter相匹配不仅仅是用来找到目标组件并激活.同时也可以用来在设备上的组件集合中发现一些特性.例如,Home应用通过寻找所有含有ACTION_MAIN和CATEGORY_LAUNCHER的activity来构造app启动器.

因此应用自己也可以用相同的方式来运用Intent匹配.PackageManager有一个query...()操作集合,它们将返回能够接收特定Intent的所有组件.还有reesolver..()方法集合可以返回最符合某一Intent的组件.例如queryIntentActivities()返回能够接收该Intent的activity列表,对应service和broadcast receiver也是一样的.但是他们都不会激活相应的组件,而是仅仅返回而已.


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值