通常一个应用会包含多个窗口,每个窗口的交互不同,功能也不同(比如图片浏览窗口或者是照相窗口等)。为了能够让用户从一个窗口跳转到另外一个窗口,你的应用必须通过Intent来定义你的应用想要做什么。当你通过startActivity()方法传递意图时,系统会通过Intent定义来启动对应的应用组件。通过intent你不仅可以跳转到自己应用的其它窗口,还可以跳转到其它应用。
一个Intent可以明确地启动一个组件(比如窗口实例)或者启动带有itentent action指定属性的某些组件(比如启动所有带有拍照功能的窗口)。
本课程将向你描述如何通过Intent在应用中交互。比如启动另一个应用,或者从其它应用中返回信息,比如从跳转到的应用中返回结果。
启动另一个应用
Android最重要的一个特性是它可以通过”aciton”将用户信息传递到另外一个应用。比如,如果你的应用有一个商业地址需要显示在地图上,你并不需要自己创建一个窗口来显示地图,而是可以通过Intent将地址传到到其它带有地图显示功能的应用。Android系统将启动这个应用并在地图上显示你传递过去的地址信息。
正如第一课”创建你的第一个Android应用”所讲,你必须通过intent才能在你应用的不同窗口之间进行跳转。你只需要指定需要跳转的窗口名称便可以跳转到其它窗口了。然而,当你想通过执行跳转到其它应用的操作时(比如跳转到显示地图的窗口),你必须使用一个间接的intent。
本课将教你如何通过间接的intent执行一个特殊的操作,通过这个action如何跳转到其它应用去。
创建一个间接的Intent
间接的Intent不需要明确地指定你需要跳转组件的名称,而是通过一个action来执行跳转操作。这个action指定你想要做什么,比如view、edit、send或者get something。Intent通常会包含这个操作的相关数据信息。比如你想查看的地址或者你想发送的电子邮件内容。这些传递的取决于你想创建什么样的intent,它有可能是一个URI,其它数据类型或者根本没有传递任何数据信息。
如果传递的数据信息是URI,这有一个简单的Intent的构造方法用于定义这个操作的数据信息。
比如,下面的例子就是通过uri来指定你想拨打的手机号:
Uri number = Uri.parse("tel:5551234");
Intent callIntent = new Intent(Intent.ACTION_DIAL, number);
当你应用通过调用startActivity()方法启动这个intent后,电话应用将会拨打传递过来的手机号码。
下面是一组通过uri跳转其它的应用的例子:
1、 查看地图:
// 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);
2、 查看网页:
Uri webpage = Uri.parse("http://www.android.com");
Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);
其它类型的间接intent需要根据不同的数据类型指定”extra”数据信息。你可以通过putExtra()方法添加一条或者多条数据信息。
默认情况下,系统通过intent的包含的uri数据来判断MIME的类型,如果你不在Intent中包含URI,你必须通过Intent的setType()方法来为其指定数据类型。设置MIME类型是为了指定哪种类型的窗口能够接收到这个intent。
下面是一些通过为intent添加extra data来指定action的例子:
通过一个attachment发送邮件:
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指定类型是非常重要的,比如你想通过ACTION_VIEW显示一张图片,你需要指定MIME类型为”image/*”,这样就可以阻止该应用查看其他类型的数据了(比如地图)。
确认是否有应用接受到了传递的Intent
虽然andorid平台能够保证通过为intent设置明确的类型后能够启动对应的内置应用程序(比如电话应用、邮件或者日历应用等),但在你请求发送一个intent之前还是有必要确认是否有应用能够接收到它。
注意:如果系统没有一个可用的应用来接收你传递的intent,你的应用将会崩溃掉。
为了确认系统有一个与你所传递的intent相对应的activity,你可以通过调用queryIntentActivitys()方法来获取与你intent类型相匹配的列表,如果返回的列表非空,你就可以安全的使用intent啦,比如:
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
boolean isIntentSafe = activities.size() > 0;
通过Intent启动一个窗口
一旦你创建了Intent实例并为其绑定了数据信息,你就可以通过调用startActivity()方法将它发送到系统中去了,如果系统核实到设备中有多于一个应用和所传递的intent想匹配时,系统将会弹出一个对话框让用户选择使用哪一个应用,正如下图所示那样。如果系统核实到当前设备只有一个应用满足条件,系统将会立即启动它。
startActivity(intent);
下面是一个查看地图的完整示例,先检查系统中是否有满足条件的应用,然后根据返回结果确定是否启动它:
// 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选择对话框
你会注意到如果当前设备中有多于一个应用和你所传递的intent相匹配时系统会弹出一个默认的应用选择对话框(对话框底部有一个用于选择的checkbox)。这对于用户期望每次都通过这个应用来启动它是非常有用的,比如当打开一个网页的时候(用户通常都会使用同一款浏览器)或者是照相(用户通常都偏好使用某一款相机应用)。
然而,如果要执行的动作可以被多个应用处理并且用户可能希望每次使用的应用都不尽相同—比如一次分享操作,用户想将一条信息分享到多个应用中—你应该像下图这样每次都明确地显示一个强制用户进行选择对话框(用户不能选择一个应用作为默认应用)。
为了能够显示这个选择对话框,你需要创建一个调用createChooser()方法的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().getString(R.string.chooser_title);
// Create and start the chooser
Intent chooser = Intent.createChooser(intent, title);
startActivity(chooser);
调用startActivity()方法后,你将会看到一个标题为你所传递过去的字符串、显示系统所有可用应用的列表对话框。
从跳转到的窗口中返回结果
启动一个窗口不仅仅只有startActivity()这一种方法,如果你想在启动一个窗口的同时还能从所跳转到的窗口中得到些什么的话,你可以使用startActivityForResult()方法。
比如,你的应用可以启动一个相机应用,并能够获取到拍摄的照片。或者是你启动了一个通讯录应用来选择联系人并将选择的结果返回。
当然,得到返回值的窗口必须要设计一个返回值用于两个窗口之间通信,通过它你可以在你窗口的onAcitivityForResult()回调来得到明确地返回信息。
启动窗口
和通过startAcitivity()方法启动一个窗口相比,除了你需要额外指定一个整型参数外,通过startActivityForResult()启动窗口和它没任何区别。
startActivityForResult()中的整型参数是一个启动窗口的请求码,当你从intent中接收到返回信息时,onActivityForResult()回调会返回一个和你所发送相同的请求码回来,用于获取对应的返回信息。
比如,下面是启动一个允许用户选择联系人的窗口:
static final int PICK_CONTACT_REQUEST = 1; // The request code
...
private void pickContact() {
Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts"));
pickContactIntent.setType(Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers
startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST);
}
接收返回值
当用户在子窗口中已经完成了所做的工作并返回到上一个窗口时,系统会调用你窗口的onActivityResult()方法,这个方法包括三个参数:
1、 和你通过startActivityForResult()传递相同的请求码;
2、 子窗口指定的请求码,要么是代表成功执行了操作的RESULT_OK,要么是由于某种原因导致操作失败的RESULT_CANCELED;
3、 一个携带返回数据信息的intent。
比如,下面是一个从联系人选择应用中返回时获取返回信息的示例:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Check which request we're responding to
if (requestCode == PICK_CONTACT_REQUEST) {
// Make sure the request was successful
if (resultCode == RESULT_OK) {
// The user picked a contact.
// The Intent's data Uri identifies which contact was selected.
// Do something with the contact here (bigger example below)
}
}
}
在上例中,从通讯录中返回所选择的某个联系人的uri。
为了能够成功地获取到所返回的信息,你必须知道intent所返回的数据格式。
读取联系人信息
上面所讲的从通讯录中获取返回信息并没有详细地讲解如何或返回信息中获取数据。因为这需要对content providers有一定了解。然而,如果你很好奇,你可以研究一下下面这个示例,它将让你了解如何从返回值中查询你需要的信息。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Check which request it is that we're responding to
if (requestCode == PICK_CONTACT_REQUEST) {
// Make sure the request was successful
if (resultCode == RESULT_OK) {
// Get the URI that points to the selected contact
Uri contactUri = data.getData();
// We only need the NUMBER column, because there will be only one row in the result
String[] projection = {Phone.NUMBER};
// Perform the query on the contact to get the NUMBER column
// We don't need a selection or sort order (there's only one result for the given URI)
// CAUTION: The query() method should be called from a separate thread to avoid blocking
// your app's UI thread. (For simplicity of the sample, this code doesn't do that.)
// Consider using CursorLoader to perform the query.
Cursor cursor = getContentResolver()
.query(contactUri, projection, null, null, null);
cursor.moveToFirst();
// Retrieve the phone number from the NUMBER column
int column = cursor.getColumnIndex(Phone.NUMBER);
String number = cursor.getString(column);
// Do something with the phone number...
}
}
}
允许其它应用启动你的窗口
上面两节课程都是讲解的如何通过的你应用来启动其它应用的窗口,但如果是你的应用提供了一个对其它应用很有用的action的话,你的应用就应该做好响应从其它应用跳转进来的响应请求的工作。比如,你研发了一个可以分享信息或者图片给朋友的社交应用,在其它应用中,点击分享可以进入到你的应用那是最有趣的事情了。
为了让其它应用能够启动你的窗口,你需要在清档文件中为你的窗口添加一个intent-filter标签。
当你的应用被安装在设备上后,系统将根据的窗口的intent filters属性添加到制定的类型中,这些类型用于支持不同应用之间的相互跳转。当某个应用通过startActivity()或者startActivityForResult()方法启动一个确定的intent时,系统将在这些分类中找到能够响应跳转操作的窗口。
添加一个Intent Filter
为了能够让系统明确你的窗口可以接收那种类型的intent,你为窗口添加的每个intent-filter都需要明确地指定类型。
如果窗口的intent filter满足以下intent属性的标准,系统就有可能会发送一个给定的Intent到拥有指定属性的窗口中。
Action
一个动作执行的名称字符串,通常是系统所定义属性中的某一种,比如ACTION_SEND或者ACTION_VIEW。
在清单文件中通过<action>标签在你的窗口中指定action属性,它的值必须是action的完整名称,而不是版本常量。
Data
对intent相关数据的描述。
在清单文件中通过<data>标签在你的窗口中指定intent属性,可以指定一个或者多个属性,你可以仅仅指定MIME类型,或者是一个URI前缀,或者是URI路径,亦或是这些属性的组合。
Cataegory
为处理窗口intent提供的一个额外方式,通常和用户的手势或者从来开始相关,系统提供了一系列的类别,但多数都很少被使用,然而,所有的intent默认都被定义为CATEGORY_DEFAULT。
在清单文件中通过<category>标签在你的窗口中指定category属性。
在你的intent filter中,你可以指定你的窗口接收那种类型的action。
比如,下面是为窗口定义了一个action为ACTION_SEND的intent。
<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"/>
<data android:mimeType="image/*"/>
</intent-filter>
</activity>
每个intent都只指定了一个action和一个数据类型,但是对于<intent-filter>中的<action>、<category>和<data>标签来说,定义多个也是没有问题的。
如果任何两组action和data是互斥的,你应该分开创建intent filter来指定那种action是可以接收的。
比如,你的窗口需要同时支持文本和图片的两个action(ACTION_SEND和ACTION_SENDTO)。你需要为你的窗口分开定义两组intent filter操作,如下:
<activity android:name="ShareActivity">
<!-- filter for sending text; accepts SENDTO action with sms URI schemes -->
<intent-filter>
<action android:name="android.intent.action.SENDTO"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="sms" />
<data android:scheme="smsto" />
</intent-filter>
<!-- filter for sending text or images; accepts SEND action and text or image data -->
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/*"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
在你的窗口中处理Intent
系统为了判断你的窗口接收什么样的action,你可以从启动它的intent中读取信息。
当你的窗口启动时,调用getIntent()来接收启动它的窗口所传递过来的信息,在窗口的整个生命周期中你都可以通过这种方式来获取Intent信息,但一般情况下,都是在onCreate或者onStart方法中使用它的。
比如:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Get the intent that started this activity
Intent intent = getIntent();
Uri data = intent.getData();
// Figure out what to do based on the intent type
if (intent.getType().indexOf("image/") != -1) {
// Handle intents with image data ...
} else if (intent.getType().equals("text/plain")) {
// Handle intents with text ...
}
}
返回结果给跳转你应用的窗口
如果你想返回信息给调用你的窗口,你只需要调用setResult方法来指定返回码和返回的intent信息,当你的操作完成后之前的窗口就可以收到这些信息了,你就可以调用finish()方法退出你的窗口了,比如:
// Create intent to deliver some kind of result data
Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri");
setResult(Activity.RESULT_OK, result);
finish();
你必须通过返回值指定返回码,通常情况下,要么是RESULT_OK,要么是RESULT_CANCELED,如果有必要的话你还可以通过intent传递额外的数据信息。
注意:默认情况下返回码的值为RESULT_CANCELED,如果用户在你设置返回值之前或者执行完操作之前按了返回按钮,之前的窗口接收到的返回值为RESULT_CANCELED。
如果你只是需要返回一个代码结果选项的整型值,你可以为其设置任何一个大于0的数为返回值。如果你不需要传递intent,你可以调用setResult()方法只传递返回码信息,比如:
setResult(RESULT_COLOR_RED);
finish();
在这种情况下,可能只有几个明确地结果,所以返回码只需要定义为0的整形数即可。当你在自己的应用中从一个窗口返回一个结果到上一个窗口时,它非常好使,因为窗口的返回值信息你可以将其定义成所有类都可以访问它的公有常量。
原文链接:Interacting with Other Apps