精通Android4学习笔记-Intent
What is an intent? The short answer may be that an intent is an action with its associated data payload.
Intent是具有相关数据负载的action(操作)。
Intents List: Invoking Google Applications on Android Devices
http://developer.android.com/guide/appendix/g-app-intents.html
Intent的组成
intent对象包含的内容:
action
data
setData(Uri data)
setDataAndType(Uri data, String type)
extra
显式Intent
带有组件名称
隐式Intent
没有组件名称,但依赖于其他部分(比如action和data)
general action
intent action和他所调用的app之间存在一对一的关系吗?
回答:intent action和他所调用的app之间有一对一的关系这是一种错觉。
action是一个抽象的概念,比如同样的Intent.ACTION_VIEW,根据data scheme的不同,可以对应于不同类别的app
Intent Action
intent data scheme
target app
Result
Intent.ACTION_VIEW
http
Browser
Intent.ACTION_VIEW
https
Browser
Intent.ACTION_VIEW
geo
Google Maps
Intent.ACTION_VIEW
google.streetview
Google Streetview
Intent.ACTION_CALL
tel
Dialer
Intent.ACTION_DIAL
tel
Dialer
Browser的activity注册了一个Intent,scheme是http和https
<activity......>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http"/>
<data android:scheme="https"/>
</intent-filter>
</activity>结论:
同样的Intent action,因为Intent data scheme不同,而对应于不同的target app;
同样的target app,也可以对应于不同的Intent action;
data的更多信息
data元素的语法:
<data android:host="string"
android:mimeType="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:port="string"
android:scheme="string" />
mimeType是一个经常用到的特性
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
</intent-filter>这个Intent过滤器声明可以理解为“调用此活动来查看一组笔记”
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
</intent-filter>这个Intent过滤器声明可以理解为“调用此活动来查看一条笔记”
Using Extra Information
除了action和data以外,Intent还可以包括extra的附加特性。
extra以键值对的形式表示。
Using Components to Directly Invoke an Activity
除了显式action启动activity,以及使用general action并借助URI来启动activity。
android还提供了一种更直接的方式来启动活动:指定活动的ComponentName,这是围绕对象的包名和类名的抽象。
setComponent(ComponentName name);
Intent intent = new Intent();
//注意,package name和class name都是fully qualified
intent.setComponent(new ComponentName("com.android.contacts","com.android.contacts.DialContactsEntryActivity");
startActivity(intent);相应的,AndroidManifest.xml中应该按照如下方式注册该activity
<activity android:name=".BasicViewActivity"
android:label="Test Activity">这里面没有Intent过滤器,直接通过类名或者组件名调用activity。这种类型的Intent被称为显式Intent。
显式Intent vs. 显式Action
Understanding Intent Categories
CATEGORY_DEFAULT
An activity can declare itself as a DEFAULT activity if it wants to be
invoked by implicit intents. If you don’t define this category for
your activity, that activity will need to be invoked explicitly every
time through its class name. This is why you see activities that get
invoked through generic actions or other action names that use
default category specification.CATEGORY_LAUNCHER
Assigning this category to an activity will allow it to be listed on
the launcher screen.
CATEGORY_HOMEAn activity of this type will be the home screen. Typically, there
should be only one activity of this type. If there are more, the
system will provide a prompt to pick one.
当使用Intent来启动Activity时,可以指定一种catagory来限定要选择的活动类型。也可以 搜索与某个catagory匹配的activity。
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);PackageManager pm = getPackageManager();
List<ResolveInfo> list = pm.queryIntentActivities(mainIntent, 0);for(ResolveInfo ri: list)
{
//ri.activityInfo.
Log.d("test",ri.toString());
String packagename = ri.activityInfo.packageName;
String classname = ri.activityInfo.name;
Log.d("test", packagename + ":" + classname);
if (classname.equals("com.ai.androidbook.resources.TestActivity"))
{
Intent ni = new Intent();
ni.setClassName(packagename,classname);
activity.startActivity(ni);
}
}Rules for Resolving Intents to Their Components
将Intent解析为组件的规则。我们讨论了action,data URI,extra,以及intent catagory.
android使用了多种策略,基于Intent过滤器来将Intent与它们的目标活动相匹配。
在此层次结构的顶部,只有一个Intent的component name。如果设置了此name,Intent就是显式Intent。对于显式Intent,重要的只是component name,Intent的所有其他方面或者特性都会被忽略。没有指定packagename的Intent是隐式的。解析隐式Intent对应的target component的规则非常多。
基本规则是,传入Intent的action,category和data特征必须匹配(或者呈现)Intent过滤器中指定的特征。
与Intent不同,一个Intent过滤器可以指定多个action,category和data特征。这意味着同一个intent filter可以满足多个Intent的需求,也就是说,一个action可以响应多个Intent。但是,匹配的含义在action,data特征和category之间各不相同。
action
data
Intent过滤器中缺少data和缺少action的情况是相反的。如果intent filter中没有action,将匹配所有内容;如果intent filter中没有data,Intent中的每部分数据都不会匹配。下面详细说明了data中的各种特征。
data mime type 这部分还不是很明白
数据类型区分大小写
subtype
可以使用*匹配所有subtypes
data scheme
intent data scheme是intent data URI的一部分,intent没有另外的设置data scheme的方法。
scheme也区分大小写。
If the data scheme of the incoming intent URI is content: or file:, it is considered a
match regardless of the intent filter scheme, domain, and pathdata authority就是host authority
<data android:host=www.somesite.com"/>
授权也区分大小写
data path
scheme,host和path协同验证传入Intent的URI,比如http://www.somesite.com/somepath,所以path host 和scheme不是孤立工作的,而是协同工作的。
路径也区分大小写。
intent category
传入的Intent中的每个category都必须存在于intent filter的catagory list中。intent filter中也可以包含更多类别。如果intent filter没有任何category,它只会与没有提交任何category的Intent匹配。
注意:Android将所有传递给startActivity()的隐式Intent视为好像它们至少包含一个类别:android.intent.category.DEFAULT.如果传入的Intent为隐式Intent,startActivity()中的代码只会搜索定义了DEFAULT category的活动。所以每个希望通过隐式Intent调用的activity都必须在其intent filter中包含该DEFAULT category。
<intent-filter>
<action android:name="com.androidbook.intent.action.ShowBasicView"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>DEFAULT category是startActivity实现的一部分,而不是intent filter的内在行为。
对于从launcher screens调用的那些activity,不需要指定DEFAULT类别,不过也可以指定DEFAULT类别。这些活动的filter中可能仅仅包含MAIN和LAUNCHER category。
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
练习使用ACTION_PICK
调用另外一个activity,有时候需要返回结果。现在我们介绍一个稍微复杂的action,该action在调用之后返回一个值。ACTION_PICK就属于这类general action。
ACTION_PICK的理念是启动一个活动来显示项列表。该activity然后应该允许用户从该列表中挑选一个项。用户挑选了项以后,activity应该向调用方返回所挑选选项的URI。这允许重用UI功能来选择某种类型的项。activity负责根据URI从ContentProvider获取数据。这也是数据应该尽可能封装到ContentProvider中的原因。
此URI的实际MIME类型应该类似于:vnd.android.cursor.dir/vnd.google.note
练习使用ACTION_GET_CONTENT
Pending Intent(挂起的Intent)
这是Intent的变体。在PendingIntent中,Android支持组件将Intent存储在一个位置供以后使用,可从该位置再次调用它。
看看如何创建PendingIntent
Intent regularIntent;
PendingIntent pi= PendingIntent.getActivity(Context context, //originating context
int requestCode, //0,1,2, 3, etc 这个参数用于区分两个PendingIntent
Intent intent, //original intent
int flags ) ;//flags
刚开始,我们一定会奇怪,创建一个PendingIntent为什么不是create方法而是get方法,而且更加奇怪的是这里是getActivity方法。
这个需要从常规Intent说起,常规Intent用于启动Activity/Service,或者调用BroadcastReceiver。使用Intent调用不同类型组件的性质是不同的。
为了解决这个问题,一个Android Context(superclass of Activity)提供了三种不同的方法。他们是
startActivity(Intent); startService(Intent); sendBroadcast(Intent);有了这些变体,如果希望存储Intent供以后重用,在收到广播后,Android如何知道启动活动/启动服务还是启动广播接收程序?
这就是我们必须在创建PendingIntent时候,显式指定其用途的原因,他也就解释了以下三种独立方法的意义:
PendingIntent.getActivity(context,0,intent,...); PendingIntent.getService(context,0,intent,...); PendingIntent.getBroadcast(context,0,intent,...);PendingIntent pi= PendingIntent.getActivity(Context context, //originating context
int requestCode, //0,1,2, 3, etc 这个参数用于区分两个PendingIntent
Intent intent, //original intent
int flags ) ;//flags
我们看看,同样内容的intent(不考虑extra包),会得到一样的PendingIntent。
original intent的内部内容相匹配(除了extra包),就将他们视为相同。extra bundle可以不同,不会影响Intent的唯一性。如果你区别两个相同的original intent的pending intent,你可以使用不同的requestCode。
flags表示在存在PendingIntent时执行的操作-返回null,改写extra等。
flags默认是0.
FLAG_CANCEL_CURRENT
, 如果已经有了一个相同内容的PI,就先取消原先的PI,再去创建一个新的PI;
FLAG_ONE_SHOT
, 这个PI仅仅只能被使用一次
FLAG_NO_CREATE
, 如果已经有了一个相同的PendingIntent,就返回null而不是去创建一个新的PI。
FLAG_UPDATE_CURRENT
, 和上面的不同之处在于,原先的PI会被更新extra data部分。If you only need one PendingIntent active at a time for any of the Intents you will use, then you can alternatively use the flags
FLAG_CANCEL_CURRENT
orFLAG_UPDATE_CURRENT
to either cancel or modify whatever current PendingIntent is associated with the Intent you are supplying.参考:http://developer.android.com/reference/android/app/PendingIntent.html
在20章的闹钟和23章的电话api中,我们都会说到PendingIntent,这对于我们加深理解PI的用法很有帮助。