您的位置:首页 > 产品设计 > UI/UE

Develop系列-API Guides-应用组件-Intents and Intent Filters

2014-08-09 00:18 281 查看

Intents 和 Intent Filters

(Intent译为意图,让人比较费解,实际上解释为“消息”更加合理,干脆就不翻译了)

Intent是能在app组件间传递的消息体,基本使用方式有如下三种:

启动activity startActivity:intent描述需要启动的activity和必须的数据
startActivityForResult:intent启动的activity结束后,会触发onActivityResult回调

启动服务 startService:intent描述需要启动的service和必须的数据
bindService

发送广播 通过Intent来的
sendBroadcast()
,
sendOrderedBroadcast()
, or
sendStickyBroadcast()
来发送广播

Intent类型

显式intents:通过指定包名+类名来明确需要启动的组件,一般用在app内部使用。
隐式intents:不指定具体的组件,通过定义一些动作或者条件,由系统来匹配能够执行动作或者满足条件的组件。



A创建intent,作为入参startActivity
Android系统搜索所有app的intent filter用于适配A发出的intent。(如果有多个匹配上,会弹框给用户选择)
Android系统通过onCreate启动匹配上的B,并把A的intent当做入参
Caution: 为了确保你的app是安全的,通常用显式intent的方式来启动service,因为用隐式intent启动服务是有安全风险的,隐式intent无法预知启动的service就是你想要的那个。

创建一个Intent

Intent主要包含如下属性:

组件名

需要启动的组件名,一般指包名+类名:com.example.ExampleActivity

对于显式intent是必选的,对于隐式intent,不能指定。





Action(动作?)

指定执行一般动作的字符串,比如:

/**
* Activity Action: 给用户显示数据。比如:在联系人中显示具体
* 联系人的信息;在邮件发件人中,显示可用的发件人列表。
* <p>Input: {@link #getData} is URI from which to retrieve data.
* <p>Output: nothing.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_VIEW = "android.intent.action.VIEW";







你可以自定义Action,一般优先使用Intent类中默认支持的Action。

在Intent构造函数或者通过setAction指定:

public Intent setAction(String action) {
mAction = action != null ? action.intern() : null;
return this;
}


public Intent(String action) {
setAction(action);
}




Data(数据)


URI一般是具体MIME类型的数据,由action来执行。比如:action是ACTION_EDIT,那么数据一般是可编辑文档的URI。

为了让系统能够更精确地适配到你发出来的Intent,在指定Data的同时最好把Type也指定。当你的数据以content:开头时,系统可以判断出具体类型。

public Intent setType(String type) {
mData = null;
mType = type;
return this;
}


public Intent setData(Uri data) {
mData = data;
mType = null;
return this;
}


public Intent setDataAndType(Uri data, String type) {
mData = data;
mType = type;
return this;
}


上述代码说明type和data如果需要同时指定,请使用setDataAndType,否则不论是setType还是setData都会将另一个设置为null。



Category(类别)


任意数量的category可以放在一个intent中,但是大多数intents不需要category。

常用的如下:

CATEGORY_BROWSABLE:activity可以通用web浏览器来显示链接索引,比如图片或者emai信息。

CATEGORY_LAUNCHER:activity是此应用初始启动的activity,并且会在launcher中显示。

public Intent addCategory(String category) {
if (mCategories == null) {
mCategories = new ArraySet<String>();
}
mCategories.add(category.intern());
return this;
}




系统可以通过componet name、action、data、category这四个属性来匹配具体启动的组件,然而intent还可以设置更多不影响组件匹配的属性,如下介绍的Extras和Flags:

Extras(额外信息)



public Intent putExtra(String name, 类型 value) {
if (mExtras == null) {
mExtras = new Bundle();
}
mExtras.put类型(name, value);
return this;
}





其中类型可以是:boolean、byte、char,short、int、long、float、double、String、CharSequence、Parcelable +上述类型的数组(类型[ ]) + Serializable、Bundle、IBinder

public Intent putExtras(Bundle extras) {
if (mExtras == null) {
mExtras = new Bundle();
}
mExtras.putAll(extras);
return this;
}
public Intent putExtras(Intent src) {
if (src.mExtras != null) {
if (mExtras == null) {
mExtras = new Bundle(src.mExtras);
} else {
mExtras.putAll(src.mExtras);
}
}
return this;
}


比如,当创建发送email的intent,可以用
EXTRA_EMAIL
来指定收件人,用
EXTRA_SUBJECT
来指定邮件主题。

可以自定义Extra_*的常量。



Flags(标记)


flags可以通知Android系统怎么启动一个activity(比如,activity的所属task)和如何对待启动后的activity(比如,activity是否属于最近activities启动列表)

public Intent setFlags(int flags) {
mFlags = flags;
return this;
}




显式intent例子



// 在Activity中执行,this表示当前Context
// fileUrl是个URL字串,比如"http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);




隐式intent例子

Caution: 隐式intent通过startActivity时,有可能系统没有任何apps可以处理你发出的intent,这会导致你的app crash,为了确保系统中至少有一个app能够处理你发出的intent,请发送intent之前先用resolveActivity进行判断。



// 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,系统会弹框给用户选择,用户可以选择一个并且将其设为默认项。

然后如果多个app可以处理你发出的隐式intent,并且用户想每次都选择不同的app来处理,有就必须强制显示选择框给用户选择。这种选择框每次都会询问用户使用哪个app,并且没法将其中一个设为默认。比较常见的例子:通过ACTION_SEND来分享数据,用户通常根据使用场景选择不同的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 (intent.resolveActivity(getPackageManager()) != null) {
startActivity(chooser);
}




接收隐式intent


在manifest文件中定义<intent-filter>可以指定app能够处理的隐式intent,当然显式intent的话只匹配组件名,其他的都直接忽略。

一组filter定义一个app能够支撑的能力

<action>


<data>


<category>
:对于activity,filter必须包含CATEGORY_DEFAULT,系统在startActivity和startActivityForResult时只会处理带CATEGORY_DEFAULT类型的activities。

<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并不是一种安全的方式来保护你的组件不被其他apps启动,何况开发者可用通过包名+类名来直接显式intent启动你的组件,如果你的组件只能由同一个app里面的其他组件来启动,那么请将exported属性设置为false

Caution:还是那句话,服务请显式启动。

Note:对于广播来说,可以在代码中动态注册和去注册,运行时指定时间内有效的广播可以用此方法。



过滤器例子



<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>




使用Pending Intent


需要预定好,但是有触发条件之后才能执行的intent,常用场景:

Notification:Android中点击每个下拉栏的通知,可以跳转到其他界面,这个通知中包含的用户点击后才执行的动作就是pendingintent,在NotificationManager中执行这个intent

App Widget:用于预先定义widget上具体需要执行的动作,比如按Music Widget上的播放按钮,来播放音乐同时刷新widget视图,滚动显示歌词,这个响应按键动作的intent就是pendingintent

AlarmManager:指定时间之后执行的intent

创建方式:

PendingIntent.getActivity()


PendingIntent.getService()


PendingIntent.getBroadcast()




Intent匹配测试

Action测试


可匹配场景:

intent包含的action在filter定义的action列表中

intent如果没有action,filter包含至少一个action

filter没有定义action,必定匹配失败



Category测试


可匹配场景:

filter中的category至少包含所有intent中的category

intent如果没有category



Data测试


每个<data>可以指定一个URI结构:<scheme>://<host>:<port>/<path>

结构中每个属性都是可选项,但是有具体关系:

scheme不匹配,host直接忽略

host不匹配,port直接忽略

scheme和host都不匹配,path直接忽略

intent中URI和filter中URI比较时,只匹配filter中的URI

filter只定义scheme,所有scheme的URIs都匹配

filter定义scheme和authority,但是没有path,那么同样scheme和authority能匹配上,而不管path

filter定义scheme、authority、path,全匹配

Note:path可用通配符*来适配

intent中URI和MIME type,与filter中URI和MIME type匹配规则:

intent定义URI或者MIME,而filter都不定义,匹配上。

intent定义URI,但没有定义MIME(没有定义,也不能从URI中识别出MIME),filter定义能匹配上的URI并且没有定义MIME,匹配上

intent定义MIME,但没有定义URI,filer定义相同的MIME,并且没有指定URI

intent定义MIME(可从URI中识别出MIME也算)和URI,filter定义的MIME必须包含intent定义的MIME,同时URI必须匹配上,有一种情况例外:intent定义的URI是以content:和file:开头的,那么filter只需要能匹配MIME,就算匹配上。



Intent匹配


intent匹配不仅仅用于启动组件,还可以有其他用途,比如Launcher通过匹配规格来查找需要在桌面显示的所有图标(activities必须包含
ACTION_MAIN
CATEGORY_LAUNCHER
category)

packageManager有一系列query…()和resolve…()方法来列出可适配对应intent的组件集合。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: