Android之Intents 和Intent Filters

496 查看


1. Intents 和Intent Filters

  • to start an Activity
    startActivity or startActivityForResults
  • to start a service
    startService or bindService
  • to deliever a broadcast
    sendBroadcast, sendOrderedBroadcast or sendStickyBroadcast

1.1 Intent Types

  • Explicit intents
    指定请求组件的全限定名,就包名+类名。
  • Implicit intents
    申明通用的动作去执行,允许其他App处理该动作。

1.2 Building Intent

  • Component name
    是决定该Intent是否为Explicit Intent,可通过set Component(), setClass(), setClassName(), 或者是作为Intent()的构造器的参数。若没指定该参数,则系统使用一下item进行匹配。其中Service必须指定Component Name。
  • Action
    是一个String字符串,指定通用动作去执行,如View or Pick。该字段通常能够决定Intent的其他字段如何构造,尤其是在data域和extras域。可自定义Action,但更推荐使用系统提供的。以下为常用的Action, ACTION_VIEW(使用地图显示地址或使用相册显示图片),ACTION_SEND(使用其他App共享数据,诸如电子邮件或者社交网络共享).
    可以通过setAction(),或者Intent的构造器进行指定。定义自己的ACTION,请加上包名,如下
    static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
  • Data
    使用URI指定MIME类型的数据,通常需要指定数据类型。但是使用content: URI的URI数据,可不指定类型,因为系统可识别出该数据位于设备上,并且受到ContentProvider控制,因此MIME类型就变为可见。
    指定方式为setData(), setType() or setDataAndType(),其中setData和setType不可同时使用,若需要,请使用setDataAndType()。
  • Category
    一个String,包含可处理该Intent的组件种类。每个Intent可包含任意数量的Category,但大部分的Intent都不需要,通常设置为android.intent.category.DEFAULT。常见的Category:CATEGORY_BROWSABLECATEGORY_LAUNCHER。前者表示该数据可为浏览器打开,后者表示该Activity是一个任务的初始Activity,并且将会被显示在Launcher里。可用'addCategory()'来指定该项。
    >Tip 以上介绍的component name, action, data, 和category限定了一个intent的特性,Android系统可知道如何决定一个组件来处理或启动。
  • Extras
    携带键值对信息,KV来完成特定的Action。可通过putExtras(K, V),或者创建一个Bundle携带所有的KVP,然后putExtras到Intent。指定自己的Extras Key,务必加上包名,如static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
  • FLAG
    定义Intent的元素据metadata,可指导Android系统如何启动一个Activity(task所属),或者启动后如何对待(是否属于其他已经启动的Activity)

1.2 Examples

  • Explicit 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);
  • Implicit Intent
    模糊的Intent,可能会没有App响应,导致App Crash。因此可通过resolveActitvity()来验证是否有Activity响应,然后再决定是否启动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(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响应,则显示列表对话框以供用户选择,如果只有一个App响应,则直接启动。
- 强制出现App选择器
如分享文件或者数据到各种App,ACTION_SEND。可使用Intent的creatChooser来操作,如下:

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(sendIntent);
}

1.3 Receiving an Implicit Intent

在Manifest文件中申明< intent-filters >标签,通过action, data, and category等字段制定Intent可接受的类型。
一个Explicit Intent无视组件所申明的接受Intent类型,直接传递过去。
不同job应申明不同的< intent-filters >,可包括action, data, and category3个字段。
-action, 指定可接受的intetn action。
-data,可接受的数据类型,URI (scheme, host, port, path, etc.) and MIME type。
-category,必须是一个Action的string值。

Tip: 为了收到Implicit Intents,必须设定CATEGORY_DEFAULT。这样, startActivity() and startActivityForResult() 才可调用,否则没有任何Implicit可启动该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>

只有三个item都通过验证,Android才会传递Intent给该Activity。对于Broadcast Receiver,可通过registerReceiver() or unregisterReceiver()动态绑定。
- Example Filters

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

1.4 Using a Pending Intent

它是对Intent的包装器,主要用途是给予其他App权限获取本进程的数据。主要用途如下:

  • Notification Manager
  • AppWidget
  • AlarmManager

由于每个Intent都是为特定的组件设计的,因此PendingIntent也同样。

  • PendingIntent.getActivity(),启动Activtiy
  • PendingIntent.getService(),启动Service
  • PendingIntent.getBroadcast(), 启动BroadcastReceiver()。

1.5 Intent Resolution

Android 系统匹配最优的组件的算法,基于以下三方面的匹配测试:

  • The intent action
  • The intent data (both URI and data type)
  • The intent category
    其中data的URI 结构和MIME 类型。
    URI:<scheme>://<host>:<port>/<path>
    ex:content://com.example.project:200/folder/subfolder/etc
    这4个元素是线性依赖的:
    >If a scheme is not specified, the host is ignored.
    If a host is not specified, the port is ignored.
    If both the scheme and host are not specified, the path is ignored.

PackageManager 可以调用以下几个方法列出可接受该Intent的组件

  • queryIntentActivities(), 返回所有相似的Activity
  • queryIntentServices(),返回相似的所有Service
  • queryIntentBroadcastReceivers(), 返回所有

1.6 Common Intents

1 AlarmClock

Action: ACTION_SET_ALARM
Extras:....
ex:

public void createAlarm(String message, int hour, int minutes) {
    Intent intent = new Intent(AlarmClock.ACTION_SET_ALARM)
            .putExtra(AlarmClock.EXTRA_MESSAGE, message)
            .putExtra(AlarmClock.EXTRA_HOUR, hour)
            .putExtra(AlarmClock.EXTRA_MINUTES, minutes);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

并其需要使用权限:
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
相应的Intent-Filter

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

2 Timer

Action:ACTION_SET_TIMER
Extras:。。。
e.g.

public void startTimer(String message, int seconds) {
    Intent intent = new Intent(AlarmClock.ACTION_SET_TIMER)
            .putExtra(AlarmClock.EXTRA_MESSAGE, message)
            .putExtra(AlarmClock.EXTRA_LENGTH, seconds)
            .putExtra(AlarmClock.EXTRA_SKIP_UI, true);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

Permission:
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
Intent-Filter:

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

3 显示所有Alarms

Action:ACTION_SHOW_ALARMS

4 添加Calendar Event

Action:ACTION_INSERT
Data URI:Events.CONTENT_URI
MIME Type: "vnd.android.cursor.dir/event"
Extras: 。。。

4 拍摄照片或摄制录像

Action:ACTION_IMAGE_CAPTURE or ACTION_VIDEO_CAPTURE
Extras:EXTRA_OUTPUT
使用onActivityResult()接收返回数据:

static final int REQUEST_IMAGE_CAPTURE = 1;
static final Uri mLocationForPhotos;

public void capturePhoto(String targetFilename) {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    intent.putExtra(MediaStore.EXTRA_OUTPUT,
            Uri.withAppendedPath(mLocationForPhotos, targetFilename);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(intent, REQUEST_IMAGE_CAPTURE);
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
        Bitmap thumbnail = data.getParcelable("data");
        // Do other work with full size photo saved in mLocationForPhotos
        ...
    }
}

相应的Intent-Filter

<activity ...>
    <intent-filter>
        <action android:name="android.media.action.IMAGE_CAPTURE" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

5 Start a camera app in still image mode

Action: INTENT_ACTION_STILL_IMAGE_CAMERA

6 Start a camera app in video mode

Action: INTENT_ACTION_VIDEO_CAMERA

7 选择一个联系人

Action:ACTION_PICK
MIME Type:Contacts.CONTENT_TYPE
e.g.:

static final int REQUEST_SELECT_CONTACT = 1;

public void selectContact() {
    Intent intent = new Intent(Intent.ACTION_PICK);
    intent.setType(ContactsContract.Contacts.CONTENT_TYPE);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(intent, REQUEST_SELECT_CONTACT);
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_SELECT_CONTACT && resultCode == RESULT_OK) {
        Uri contactUri = data.getData();
        // Do something with the selected contact at contactUri
        ...
    }
}

8 选择特定的联系人

Action: ACTION_PICK
MIME Type:
CommonDataKinds.Phone.CONTENT_TYPE。Pick from contacts with a phone number.
CommonDataKinds.Email.CONTENT_TYPE,Pick from contacts with an email address.
CommonDataKinds.StructuredPostal.CONTENT_TYPE,Pick from contacts with a postal address.

static final int REQUEST_SELECT_PHONE_NUMBER = 1;

public void selectContact() {
    // Start an activity for the user to pick a phone number from contacts
    Intent intent = new Intent(Intent.ACTION_PICK);
    intent.setType(CommonDataKinds.Phone.CONTENT_TYPE);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(intent, REQUEST_SELECT_PHONE_NUMBER);
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_SELECT_PHONE_NUMBER && resultCode == RESULT_OK) {
        // Get the URI and query the content provider for the phone number
        Uri contactUri = data.getData();
        String[] projection = new String[]{CommonDataKinds.Phone.NUMBER};
        Cursor cursor = getContentResolver().query(contactUri, projection,
                null, null, null);
        // If the cursor returned is valid, get the phone number
        if (cursor != null && cursor.moveToFirst()) {
            int numberIndex = cursor.getColumnIndex(CommonDataKinds.Phone.NUMBER);
            String number = cursor.getString(numberIndex);
            // Do something with the phone number
            ...
        }
    }
}

9 显示一个联系人

  • ACTION_PICK返回的contactURI,然后使用ACTION_VIEW查看。
  • 直接访问联系人列表,需要READ_CONTACTS权限
public void viewContact(Uri contactUri) {
    Intent intent = new Intent(Intent.ACTION_VIEW, contactUri);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
} 

10 编辑一个联系人

Action:ACTION_EDIT
URI: 之前ACTION_PICK返回的contactURI。
Extras:ContactsContract.Intents.Insert中定义的条目都可编辑

public void editContact(Uri contactUri, String email) {
    Intent intent = new Intent(Intent.ACTION_EDIT);
    intent.setData(contactUri);
    intent.putExtra(Intents.Insert.EMAIL, email);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

11 插入一个联系人

Action:ACTION_INSERT
MIME Type:Contacts.CONTENT_TYPE
Extras:One or more of the extras defined in ContactsContract.Intents.Insert.
e.g.:

public void insertContact(String name, String email) {
    Intent intent = new Intent(Intent.ACTION_INSERT);
    intent.setType(Contacts.CONTENT_TYPE);
    intent.putExtra(Intents.Insert.NAME, name);
    intent.putExtra(Intents.Insert.EMAIL, email);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

12 Email

13 File Storage

14 Fitness

15 Local Actions

16 Maps, show a location on a map

Action: ACTION_VIEW
URI: geo:latitude,longtitude
e.g.:

public void showMap(Uri geoLocation) {
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setData(geoLocation);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

intent filter:

<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="geo" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

17 MUSIC or VIDEO

Action: ACTION_VIEW
Data URI Scheme:

  • file:
  • content:
  • http:

MIME Type:

  • "audio/*"
  • "application/ogg"
  • "application/x-ogg"
  • "application/itunes" Or any other that your app may require.

example intent filter:

<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <data android:type="audio/*" />
        <data android:type="application/ogg" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

18 Phone

Action: ACTION_DIAL , ACTION_CALL
其中ACTION_DIAL,需要传输一个号码,然后跳转到拨打界面,需要点击拨打按钮。
而ACTION_CALL,需要传输一个号码,然后直接开始拨打,但是需要权限
<uses-permission android:name="android.permission.CALL_PHONE" />
Data URI Scheme:'tel:< phone-number>,voicemail:< phone-number>'
e.g.:

public void dialPhoneNumber(String phoneNumber) {
    Intent intent = new Intent(Intent.ACTION_DIAL);
    intent.setData(Uri.parse("tel:" + phoneNumber));
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

19 Settings

Action:

  • ACTION_SETTINGS ACTION_WIRELESS_SETTINGS
  • ACTION_AIRPLANE_MODE_SETTINGS ACTION_WIFI_SETTINGS
  • ACTION_APN_SETTINGS ACTION_BLUETOOTH_SETTINGS ACTION_DATE_SETTINGS
  • ACTION_LOCALE_SETTINGS ACTION_INPUT_METHOD_SETTINGS
  • ACTION_DISPLAY_SETTINGS ACTION_SECURITY_SETTINGS
  • ACTION_LOCATION_SOURCE_SETTINGS ACTION_INTERNAL_STORAGE_SETTINGS
  • ACTION_MEMORY_CARD_SETTINGS

20 Text Messaging

Action: ACTION_SENDTO, ACTION_SEND, ACTION_SEND_MULTIPLE
Data URI Scheme:

sms:<phone_number>
smsto:<phone_number>
mms:<phone_number>
mmsto:<phone_number>

MIME Type:
PLAIN_TEXT_TYPE ("text/plain")
"image/"
"video/
"
Extras:
"subject",'sms_body','EXTRA_STREAM'

e.g.:

public void composeMmsMessage(String message, Uri attachment) {
    Intent intent = new Intent(Intent.ACTION_SENDTO);
    intent.setType(HTTP.PLAIN_TEXT_TYPE);
    intent.putExtra("sms_body", message);
    intent.putExtra(Intent.EXTRA_STREAM, attachment);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

若想要确保intent被短信应用处理,使用 ACTION_SENDTO,并且设置data schmem为‘smsto:’.
相应的Intent-filter

<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <data android:type="text/plain" />
        <data android:type="image/*" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

21. WebBrowser

加载网址
- Action: ACTION_VIEW
- Data URI Scheme:
http:< URL>
https:< URL>
- MIME Type:
PLAIN_TEXT_TYPE ("text/plain")
"text/html"
"application/xhtml+xml"
"application/vnd.wap.xhtml+xml"

Web查询
Action: ACTION_WEB_SEARCH
Extras:SearchManager.QUERY The search string.

public void searchWeb(String query) {
    Intent intent = new Intent(Intent.ACTION_SEARCH);
    intent.putExtra(SearchManager.QUERY, query);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

22 Verify Intents with the Android Debug Bridge

  1. Set up an Android device for development, or use a virtual device.
  2. Install a version of your app that handles the intents you want to support.
  3. Fire an intent using adb:
    adb shell am start -a <ACTION> -t <MIME_TYPE> -d <DATA> \ -e <EXTRA_NAME> <EXTRA_VALUE> -n <ACTIVITY>
    For example:
    adb shell am start -a android.intent.action.DIAL \ -d tel:555-5555 -n org.example.MyApp/.MyActivity
    If you defined the required intent filters, your app should handle the intent.