android 最重要的一个特性就是可以在一个APP的界面,利用”action”动作把用户导向另外一个APP。比方说,你的APP想在地图上展示一个商业地址,你不需要自己去创建一个activity来显示这个地图。可以Intent来创建一个请求来在地图上展示这个地址,android系统会根据这个intent来启动相应的地图应用,显示你要看的这个地址。
就像我们在第一节:创建你的第一个APP 里面说的,你必须使用Intent 使得用户可以在你的app里面的activity之间跳转,一般都是创建一个明确里面定义了要启动的类名的intent。但是,如果我想要启动一个另外的app,比方说“访问一个地图”,那这里需要使用一个模糊的intent。
这一节说明下如何创建一个模糊的intent来执行特别的动作,用它来启动另外一个app里面符合这项动作的activity。
创建一个含蓄的Activity
函数的intent的不需要明确的指出要启动的activity类名,但是需要指定一个动作。这个动作指明了你想要做的事情,例如访问,编辑,发送或者获取某些内容。Intent也可以附加额外的数据信息给这个动作,比方说上面的你要查看的地图的地址,或者是要发送的邮件地址。根据你创建的Intent 类型,这个数据可以是Uri,或者是其他的一些类型,也有可能这个intent不需要任何数据。
如果要传入的数据是一个Uri,那么可以直接使用Intent的一个简单的构造函数。下面就是一个利用Uri指明要拨打的电话号码,利用Uri创建一个intent,进行拨打电话
Uri number = Uri.parse("tel:5551234");
Intent callIntent = new Intent(Intent.ACTION_DIAL, number);
然后可以调用startActivity(),把这个Intent作为参数,然后这个APP就对执行电话拨打,拨打上面指定的号码:5551234
下面是2组其他的Uri内容创建Intent动作的例子:
访问地图:
// Map point based on address
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
// Or map point based on latitude/longitude
// 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需要”extra”数据,可以是不同类型的数据,比方说一个string类型的。可以使用putExtra()添加一个或者多个exta数据。
默认的,系统会根据intent里面的Uri来决定一个intent需要的合适MIME(多用途扩展)类型.如果没有使用Uri,那么需要用
setType()来指定和这个intent的关联的数据类型。
设置MIME类型,能进一步的确定那种activity需要接收你的这个intent。
这里是一些添加extra数据来指定想要执行的动作的intent:
发送一个带有附件的邮件:
Intent emailIntent = new Intent(Intent.ACTION_SEND);
// The intent does not have a URI, so declare the "text/plain" MIME type
emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"jon@example.com"}); // recipients
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"));
// You can also attach multiple items by passing an ArrayList of Uris
创建一个日历事件:
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的时候,尽可能的明确这个Intent的动作。比方说你想使用ACTION_VIEW intent来展示一张图片,你应该指定MIME类型:image/* . 这样可以防止那些同样接收ACTION_VIEW动作但是是用来访问其他的类型数据(比方说地图)的APP被触发.
验证存在一个APP可以接收指定的Intent
尽管android平台可以保证谋几个Intent是肯定可以被android内建的APP(如电话,邮件,或者日历)处理,但是我们应该在换起一个intent之前先判断是否有一个APP会支持接收这个intent.
警告:如果你用startActivity唤起一个Intent,但是没有一个APP可以接收处理这个Intent的时候,你的APP会奔溃。
判断是否有一个activity可以对这个intent做出回应,可以调用queryIntentActivities()获取一个有能力处理这个intent的activity列表。如果这个函数返回的list不是空,那么就可以放心的唤起这个intent.如下:
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
boolean isIntentSafe = activities.size() > 0;
如果isIntentSafe是true,那么就是说至少有一个APP是可以接收处理这个intent的。如果是false,那么就是没有intent可以处理这个intent.
注意:一般来说,这个检查要在activity一开始启动的时候就执行,这样就可以在用户意图使用某种不支持的intent之前就禁止掉这个特性,比方说隐藏掉触动这个intent的按钮或者图标。如果你知道哪个APP可以支持某个intent,但是手机又没有安装,可以提供一个连接地址给用户去下载。关于这点,请阅读:link to your product on Google Play
利用Intent启动Activity
当创建好intent, 而且设置了extra信息,那就可以调用startActivity()把这个intent发送给系统。系统会识别出可以处理这个intent的activity,可能会有多个,系统会显示一个对话框,里面有多个可选的APP,这些APP里面都有可以处理这个intent的activity,你可以选择一个。
如图1所示:
这个就是一个对话框,里面有多个可以处理的传递给系统的intent的APP
如果只有一个activity支持处理这个intent,系统会直接启动这个对应的APP:
startActivity(intent);
下面是一个完整的例子,描述了如何创建一个访问地图的intent,判断是否有APP可以处理它:
// Build the intent
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
// Verify it resolves
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0);
boolean isIntentSafe = activities.size() > 0;
// Start an activity if it's safe
if (isIntentSafe) {
startActivity(mapIntent);
}
显示一个APP选择器
当startActivity利用一个intent启动activity的时候,可能会有多个app会对这个intent做出反应,用户可以选择一个app作为默认启动(这个默认项可以在第一次启动的时候选额,看图1,下面的那个勾选框).这样做的好处是用户可以指定在执行某个常用的action的时候,直接启动那个默认的app,而不需要去反复选择。比方说访问网页(用户可能只想用某种浏览器打开),还有拍照(用户可以指定要启动的相机应用)。然而如果要进行的action可以被多个app处理,用户可能每次都想要自己的启动不同的APP,比方说分享动作,用户可能想通过多个APP来分享一条信息,你要显示给用户一个对话框,强迫用户每次去选择一个APP来处理这个动作,用户没办法在这个选择器里面进行设置默认的操作。
想要显示一个选择器,首先用createChooser()创建一个Intent,然后把这个intent传给startActivity()。如下:
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().getText(R.string.chooser_title);
// Create and start the chooser
Intent chooser = Intent.createChooser(intent, title);
startActivity(chooser);
这样就会显示出一个对话框,里面是可以响应传递给createChooser 那个intent的所有APP,可以为这个对话框设置一个标题,像上面用title设定。