Android官方文档之App Components(Intents and Intent Filters)

Android应用框架鼓励开发者在开发应用时重用组件,本文将阐述如何用组件构建应用程序以及如何用intent将组件联系起来。
如需阅读官方原文,请您点击这个链接:

《App Components》。

您还可以参考这些博文:

  • 《Using DialogFragments》

  • 《Fragments For All》

  • 《Multithreading For Performance》

以及这些Training:

  • 《Managing the Activity Lifecycle》

  • 《Building a Dynamic UI with Fragments》

  • 《Sharing Content》


Intent 与 Intent Filters(Intents and Intent Filters)

Intent是一个传递消息的对象,您可以为Intent指定action来启动其他应用组件,Intent使组件之间通信更加便利,并且通信方式有很多,这里列举了主要的三点:

  • 启动Activity:

    您可以将intent作为参数调用startActivity()方法启动一个activity。该intent描述了将要启动的目标activity的特性并携带必要的数据信息。您还可以调用startActivityForResult()方法回传信息。

  • 启动Service:

    Service用于在后台执行任务,不与用户交互。您可以使用startService()方法执行一次性的操作(比如后在台下载一个文件),这需要Intent参数。
    如果组件之间需要向CS结构一样通讯,您可以把Service想成一端,并调用bindService(),这同样需要Intent参数。

  • 传递一个broadcast:
    broadcast是一种可以被任何应用程序截获的广播机制。系统会基于当前发生的事件发出各式broadcast(比如设备开机时、开始充电时等),您可以调用sendBroadcast(),、sendOrderedBroadcast()、 sendStickyBroadcast()方法发送一条广播,这需要传入Intent参数。


Intent的种类(Intent Types)

  • 显式Intent:通过指定具体类名启动一个组件。显式Intent一般用于同一应用程序内,因为您可以确定地知道要启动的组件名。另外,Android 5.0以后规定必须显式启动Service。

  • 隐式Intent:当希望启动具备某种特性的组件时,可以使用隐式Intent,隐式Intent无需指定类名,通常用于启动其他应用程序的组件,比如您打算启动一个地图定位的activity。


当您隐式地启动一个service或activity时,Intent会根据其中的内容,匹配其他组件中manifest文件的Intent-filter,启动符合条件的组件,并把Intent中的参数传过去,如果有多个intent-filter满足条件,那么系统会弹出一个对话框,由用户决定启动哪个组件。下面是intent与intent-filters配合启动组件的示意图:
Android官方文档之App Components(Intents and Intent Filters)_第1张图片


见上图:
1、首先Activity A利用传入的Intent调用startActivity();
2、系统会根据该Intent的条件搜索Android系统中所有匹配的组件;
3、若找到了匹配intent的intent-filters所属的组件(Activity B),则启动该组件,并回调onCreate()方法,同时将Intent传递过去。


intent-filters是manifest文件中组件内部的一个标签,该标签描述了组件具备什么特性,如果您未配置intent-filters,那个该组件只能被显式启动。


创建Intent对象(Building an Intent)

Intent中包含了目标组件需满足的特性。Intent中应包含以下信息:

  • Component name:目标组件的名字。对于显式启动,这是不可缺省的,您可以使用Intent的构造方法传入组件名称,也可以调用setComponent(), setClass(), setClassName()这些方法传入组件名;若是隐式启动,这是可选的,但intent应包含其他信息(action、category、data);

  • Action:是一个可以指明目标组件行为的字符串。action很大程度上决定了category和data中应传入的信息;您也可以在自己的应用程序组件中指定action,以便让其他应用程序启动自己的组件。对应action中字符串,不建议使用硬编码的形式,而应在所属组件的类中设置为常量。

    常见的action有:

    • ACTION_VIEW:用ACTION _VIEW启动的activity一般可以向用户展示一些信息,比如启动一个相册APP中展示图片的activity,或是启动一个地图APP中展示地址信息的activity。
    • ACTION_SEND:一般需要向 通过ACTION _SEND启动的activity 附带着发送一些信息,这些信息由由目标activity决定该发送给谁,比如社交类APP或是发送邮件的APP。

您可以将action作为参数传入Intent的构造方法或setAction()方法中。
如需定义在自己的组件中定义action,应以应用的包名作为前缀,比如:

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
  • Data:一个URI对象是一个引用的data的表现形式,或是data的MIME类型;data的类型由Intent的action决定,比如说若action是ACTION_EDIT,那么data的URI应指向一个可编辑的文件。当创建一个Intent时,除了为data指定URI以外,还应该指定data的MIME类型,比如说,一个用于展示图片的activity是不能用来放音乐的,如果您要启动这个activity,就需要将data的MIME类型指定为”image/png”、”image/jpeg”等。有些时候,从data的URI中就能推断出MIME的类型,比如当一个URI的schema是”content://”时,表明该URI指向了设备内部的一个文件并由ContentProvider管理着,系统可以根据该文件推断出data的MIME类型。
    您可以调用setData()方法设置URI,调用setType()方法设置MIME类型,或调用setDataAndType()方法同时设置URI和MIME类型。

    !请注意:如果您需要同时设置URI和MIME类型,只能调用setDataAndType()方法,而不能分别调用setData()和setType(),因为调用setData()时会首先将setType()中的内容置空,反之亦然( they each nullify the value of the other)

  • Category:是一个字符串,表示目标组件的附加信息,大部分intent不需要category。以下是依稀而常用的category:

    • CATEGORY_BROWSABLE:表示目标activity可以被网页上的某个链接启动,如图片activity或e-mail信息activity。
    • CATEGORY_LAUNCHER:目标activity是任务栈的第一个activity,也就是应用程序的启示activity。

    您可以将category参数传入addCategory()方法中。

上述的参数(component name, action, data, and category)代表了intent的属性,通过这些参数,系统可以筛选出符合条件的目标组件。除此之外,intent还可以包含下列参数,与上面的参数不同的是,系统不会使用这些参数来筛选目标组件:

  • Extras:一些intent可以携带的附加信息,以键值对的形式存储。可以使用putExtra()方法将键值对信息传入,也可以将键值对信息放在Bundle对象中,再通过将Bundle对象传入putExtra()中。
    Intent类中封装了许多 ” EXTRA_* “形式的标准extra,如果想封装自己的extra键,请您以应用程序的报名作为前缀,比如:
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
  • Flags:该参数可以为intent添加元数据(meta-data),flag可以指导系统以何种方式启动一个activity、是否将启动的activity放在该应用的任务栈中,等等。

隐式Intent的例子(Example implicit intent)

!请注意:若系统中没有满足隐式Intent的目标组件,则应用将崩溃(crash),所以首先应判断,在调用startActivity()。
以下是一个通过隐式intent启动一个“发送信息的Activity”的例子:

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

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

如果有一个目标组件满足intent,则启动该组件;若有多个满足intent的目标组件,则系统弹出一个列表以供选择。


使用应用选择器(Forcing an app chooser)

正如向上面说,系统中可能存在多个目标组件满足隐式intent,这时会弹出一个列表供用户选择,有些时候,用户希望每次都启动一个相同的组件(比如用户每次都想启动chrome浏览器而不是系统自带的浏览器),这时只需要勾选“不再询问”选项就行了,下次再启动时,列表将不再弹出;还有些时候,用户每次都需要对列表中的Activity进行筛选,比如启动用于分享的Activity,用户希望每次分享到不同的平台,这时需要调用Intent.createChooser()方法以保证每次都弹出选择列表,如下所示:

Intent sendIntent = 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 the chooser dialog
Intent chooser = Intent.createChooser(sendIntent, title);

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

接收隐式Intent(Receiving an Implicit Intent)

通过在manifest文件中配置intent-filter标签中的action, data, and category,可以设置筛选信息,只有同时符合上述三个标签设置的筛选信息,Intent才能开启您的应用程序组件:

  • action标签:
    可匹配Intent中的action参数。

  • data标签:
    可匹配Intent中的data参数(URI地址以及MIME 类型)。

  • category标签:
    可匹配Intent中的category 参数。
    !请注意:如组件需要被隐式启动,必须配置CATEGORY_DEFAULT
    如想隐式启动一个分享的Activity,则目标Activity如下配置:

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

一个intent-filter中可以包含多个 action, data, category 标签。

若组件仅希望通过本应用启动,可将组件中的exported属性设为false。
!请注意:为了避免隐式intent匹配上了您的Service组件,请不要在service中配置intent-filter(Service必须显式启动)


intent-filter举例(Example filters)

下面是一个社交APP的manifest文件示例:

<activity android:name="MainActivity">
    
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    intent-filter>
activity>

<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>
    
    <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为 “android.intent.action.MAIN”表示该Activity是应用的主入口,且无需配置data。
category为 “android.intent.category.LAUNCHER”表示该activity的启动图标(通过icon属性配置)应添加到系统的launcher中,若未配置icon,则会使用application标签下的icon。
以上两个属性应成对出现。

如需隐式启动ShareActivity,仅需匹配一个intent-filter就行了。


使用Pending Intent(Using a Pending Intent)

PendingIntent是一个包装Intent的类,主要用于实现Intent的延时启动,PendingIntent的主要使用场合:

  • 包装一个Notification的启动Intent;
  • 包装一个App Widget的Intent(按Home键启动的activity);
  • 包装一个延时启动的activity(如AlarmManager)。

使用PendingIntent启动的activity无需使用startActivity()就能启动,您应当使用对应组件的方法启动相应组件:

  • 通过PendingIntent.getActivity()启动一个activity;
  • 通过PendingIntent.getService()启动一个Service;
  • 通过PendingIntent.getBroadcast()启动一个BroadcastReceiver;

解析Intent(Intent Resolution)

目标组件通过以下三点匹配相应的Intent:

  • The intent action;
  • The intent data (both URI and data type);
  • The intent category。

匹配Action(Action test)

intent filter可定义零到多个action标签:


    "android.intent.action.EDIT" />
    "android.intent.action.VIEW" />
    ...

intent需要匹配上其中一个action标签。如果intent-filter中没有action标签,则intent无需action就能匹配。


匹配Category(Category test)

intent filter可定义零到多个category标签:


    "android.intent.category.DEFAULT" />
    "android.intent.category.BROWSABLE" />
    ...

intent中的定义的每一个category都需要匹配上intent-filter中的category标签,反之不成立(intent-filter中的category标签可能比intent中的定义的category多)。所以无论intent-filter中是否定义了category标签,未添加category的intent总能匹配上该intent-filter。
!请注意:通过startActivity()或startActivityForResult()方法隐式启动的intent中,将自动被添加一个CATEGORY_DEFAULT的category,所以若您希望自己的activity能够被隐式启动,则需要在intent-filter中添加一个android.intent.category.DEFAULT的category标签。


匹配Data(Data test)

intent filter可定义零到多个data标签:


    "video/mpeg" android:scheme="http" ... />
    "audio/mpeg" android:scheme="http" ... />
    ...

每个data标签都能设置mimeType和URI 结构,其中URI可分成四部分:scheme, host, port 和 path;其结构如下:

://:/

比如:

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

其中

  • scheme为content;
  • host为com.example.project;
  • port为200;
  • path为folder/subfolder/etc。

每一部分在data标签中都不是必须定义的,但存在一个线性依赖:

  • 若scheme 未指定,则host被忽略;
  • 若host未指定,则port被忽略;
  • 若scheme和host均未指定,则path被忽略;

在intent中添加的data只需要匹配一部分intent-filter中的data(URI匹配):

  • 若filter只定义了scheme,则intent的data定义的URI中只要包含了相同的scheme,就能匹配;
  • 若filter只定义了scheme和host,则intent的data定义的URI中只要包含了相同的scheme和host,就能匹配;
  • 若filter只定义了scheme、host和port,则intent的data定义的URI中只要包含了相同的scheme、host和port,就能匹配;

intent-filter匹配intent的data中URI和mimeType类型的规则如下:

  1. 如果intent-filter中未指定data,则未添加data的intent可以匹配;
  2. 如果intent-filter中指定了URI,但未指定mimeType,则按照上一段的规则匹配(intent中也应未指定mimeType);
  3. 如果intent-filter中指定了mimeType,而未指定URI,则可以匹配intent中指定了相同mimeType,而未指定URI的组件;
  4. 如果intent-filter中同时指定了mimeType和URI,则:
    • intent中添加的mimeType只要能匹配上intent-filter中的某一个mimeType,就能匹配上mimeType部分;
    • intent中添加的URI则按照上一段中的URI匹配规则,就能该匹配上URI部分;特别地:若intent中的URI的scheme 指定为content: 或者 file:,那么即便intent-filter中未定义URI,也能匹配成功。换句话说:包含content: 或者 file: 的URI总是能匹配上只定义了mimeType的intent-filter

你可能感兴趣的:(Android进阶,Android官方文档翻译)