Android Training - 跟其他程序交互(1) - 发送用户请求给另外一个程序

Android Training - 和其他程序交互(1) - 发送用户请求给另外一个程序
Android很重要的一个特征就是,一个程序可以发送用户请求给其他程序去执行特定的动作。例如,如果你想在地图上显示你的公司地址,你不需要建立一个显示地图的activity,你可以使用intent创建一个显示地图的请求,Android系统会开启一个程序显示地图上这个地址。

像第一个教程:Android Training - 建立你的第一个应用程序所说的,你必须使用intent操纵你的activity。通常在程序内部都使用显式的intent,它定义你想启动的组件的明确类名,但是,如果你想在其他程序执行一个动作,比如“显示地图”,你必须使用一个隐式的intent。

这个课程展示了怎么为一个特定动作创建一个隐式的intent,怎么使用它去启动一个activity,然后在其他程序中执行这个特定的动作。

创建一个隐式Intent

隐式Intent不是声明组件的类名去启动组件,而是声明一个执行的动作。这些动作指定了你想做的事情,例如,view, edit, send, get。通常也包含和动作关联的数据,比如你想显示的地址,你想发送的电子邮件内容。依靠你创建的intent,数据可能是一个Uri,或者其他类型,或者intent根本不需要任何数据。

如果你的数据是Uri,这有一种简单的Intent()构造函数,你可以使用构造函数定义动作和数据。

例如,下面的代码展示了怎么创建一个intent,使用Uri数据指定电话号码,初始化一个电话呼叫动作:
Uri number = Uri.parse("tel:5551234");
Intent callIntent = new Intent(Intent.ACTION_DIAL, number);
当程序使用startActivity()调用这个intent时,通话程序取得这个号码开始一个呼叫。

下面是一些使用Uri数据的其他例子:
  • 显示地图:
    // 基于地址的定位
    Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
    // 基于经纬度的定位
    // Uri location = Uri.parse("geo:37.422219,-122.08364?z=14"); // z param is zoom level
    Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
  • 显示网页:
    Uri webpage = Uri.parse("http://www.android.com");
    Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);
隐式的intent也支持其他类型的“额外”数据,例如一个字符串。你可以使用putExtra()函数添加一个或者多个额外数据。

默认情况下,系统通过Uri数据能够指定合适的MIME类型,如果你不使用Uri,那么你需要使用setType()方法指定intent关联的数据类型。设置MIME类型可以促进指定那个activity可以接收这个intent。

下面有一些添加了额外数据的intent:
  • 发送带附件的邮件:
    Intent emailIntent = new Intent(Intent.ACTION_SEND);
    // intent没有URI, 所以声明"text/plain" MIME类型
    emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);
    emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"jon@example.com"}); // 收件人
    emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Email subject");
    emailIntent.putExtra(Intent.EXTRA_TEXT, "Email message text");
    emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://path/to/email/attachment"));
    // 你也可以通过传递一个Uri数组附加多类型的数据
  • 创建一个事件日程表:
    Intent calendarIntent = new Intent(Intent.ACTION_INSERT, Events.CONTENT_URI);
    Calendar beginTime = Calendar.getInstance().set(2012, 0, 19, 7, 30);
    Calendar endTime = Calendar.getInstance().set(2012, 0, 19, 10, 30);
    calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis());
    calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis());
    calendarIntent.putExtra(Events.TITLE, "Ninja class");
    calendarIntent.putExtra(Events.EVENT_LOCATION, "Secret dojo");
    提示:这个时间日程表只支持API级别14或者更高版本。
提示:尽可能详细的指定你的intent很重要。例如,如果你想使用ACTION_VIEW显示一个图片,你需要定义MIME类型为image/*。防止程序触发intent时显示其他类型的数据。

验证是否有程序能够接收这个Intent

虽然Android包含了一下内置接收intent的程序(例如,通话,发邮件,日程表),在调用intent前,你还是需要一个验证步骤。

警告:如果你的设备中不包含一个能够处理你指定的intent的程序,你的程序将会崩溃。

验证是否有程序能够接收这个intent,你可以调用queryIntentActivities()取得一个能够处理你的intent的activity列表。如果返回的列表不是空的,那么你可以安全的使用这个intent,例如:
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
boolean isIntentSafe = activities.size() > 0;
如果isIntentSafe为true,那么至少有一个程序可以接收这个intent。如果为false,那么没有任何程序可以处理这个intent。

提示:你应该在activity第一次启动的时候执行检查,假如使用前没有检查,你应该禁止用户使用这个intent。如果你知道一个特定程序能够处理这个intent,你也可以提供一个程序的下载链接让用户下载(看 link to your product on Google Play)。

使用Intent开始一个Activity

一旦你已经创建好你的intent,并且已经设置了附加信息,那么通过startActivity发送这个intent给系统。如果系统识别出多于一个的activity能处理这个intent,它会展示一个选择对话框,用户可以选择自己想要的程序。如下图。如果只有一个activity能够处理这个intent,就会直接运行。
Android Training - 跟其他程序交互(1) - 发送用户请求给另外一个程序
startActivity(intent);
Android Training - 跟其他程序交互(1) - 发送用户请求给另外一个程序

下面是一个完整的例子,展示怎么创建一个intent去显示地图,检查有程序可以处理这个intent,然后启动它:
// 创建intent
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);

// 检查是否可用
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0);
boolean isIntentSafe = activities.size() > 0;
  
// 安全开始
if (isIntentSafe) {
    startActivity(mapIntent);
}
显示一个程序选择器

当你使用startActivity开始一个activity时,如果有多个程序符合这个intent,用户可以选择一个程序作为默认程序(见上图,有一个复选框在对话框的下面)。可以每次都使用相同的程序运行,对用户来说是很友好的体验,例如,打开网页(使用一个浏览器)或者拍照(使用喜欢的摄像头)时。不管怎样,如果一个动作适合多程序运行,用户可能也需要每次选择不同的程序 -- 例如一个“分享”动作,用户可能有很多可以执行分享的程序 -- 你应该明确的显示一个选择对话框,让用户每次都可以选择他们想要的程序(不能选择一个默认项)。
Android Training - 跟其他程序交互(1) - 发送用户请求给另外一个程序
Android Training - 跟其他程序交互(1) - 发送用户请求给另外一个程序

要实现选择器,你需要使用createChooser()创建intent,例如:
Intent intent = new Intent(Intent.ACTION_SEND);
...

// 从字符串资源取得标题
String title = getResources().getText(R.string.chooser_title);
// 创建并开始选择器
Intent chooser = Intent.createChooser(intent, title);
startActivity(chooser);
传递intent和对话框标题给createChooser()函数,显示一个程序列表对话框。