一. Getting Started
1.5 Interacting with Other Apps
Android 应用一般具有若干个Activity。每个 Activity 显示一个用户界面,用户可通过该界面执行特定任务(比如,查看地图或拍照)。要将用户从一个 Activity 转至另一 Activity,您的应用必须使用 Intent 定义您的应用做某事的 “Intent”。
1.5.1 Sending the User to Another App
构建隐式 Intent
- 使用指定电话号码的 Uri 数据创建发起电话呼叫的 Intent:
Uri number = Uri.parse("tel:5551234");
Intent callIntent = new Intent(Intent.ACTION_DIAL, number);
- 查看地图:
// 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 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 的应用
调用 queryIntentActivities()
来获取是否有能处理该 Intent 的 Activity 列表。如果返回的 List 不为空,可以安全使用该 Intent。
PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;
启动具有 Intent 的 Activity
此处显示完整的示例:如何创建查看地图的 Intent,验证是否存在处理 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);
}
显示应用选择器
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 (intent.resolveActivity(getPackageManager()) != null) {
startActivity(chooser);
}
1.5.2 startActivityForResult
启动 Activity
例如,此处显示如何开始允许用户选择联系人的 Activity:
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);
}
接收结果
Activity 返回结果时会调用 onActivityResult()
方法,该方法有三个参数:
- requestCode:请求码,作为标记;
- resultCode:结果码,如果操作成功返回 RESULT_OK,失败则 RESULT_CANCELED;
- data:传送数据的 Intent 对象。
本例说明您可以如何处理“选择联系人” 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)
}
}
}
成功获取联系人数据后的进一步操作:
@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...
}
}
}
注意读取联系人需要申请相应权限
1.5.2 Allowing Other Apps to Start Your Activity
要允许其他应用启动您的 Activity,您需要在清单文件中为对应的 <activity>
元素添加一个 <intent-filter>
元素。
当您的应用安装在设备上时,系统会识别您的 Intent 过滤器并添加信息至所有已安装应用支持的 Intent 内部目录。当应用通过隐含 Intent 调用 startActivity()
或 startActivityForResult()
时,系统会找到可以响应该 Intent 的 Activity。
添加 Intent 过滤器
Activity 基本 Intent 过滤器:
- 操作 <action> 标签:表示要执行的操作名,如 ACTION_SEND 或 ACTION_VIEW。
- 数据 <data> 标签:与 Intent 关联的数据描述。可以指定 MIME 类型、URI 前缀、URI 架构或这些的组合以及其他指示所接受数据类型的项。
注:如果您无需声明关于数据的具体信息 Uri 比如,您的 Activity 处理其他类型的“额外”数据而不是 URI 时),您应只指定
android:mimeType
属性声明您的 Activity 处理的数据类型,比如text/plain
或image/jpeg
。
- 类别:<category> 标签:提供另外一种表征处理 Intent 的 Activity 的方法,通常与用户手势或 Activity 启动的位置有关。 经常使用默认的 CATEGORY_DEFAULT 进行定义。
声明 Activity 的 Intent 过滤器的各项属性,比如下方的一个接收收据类型为文本或图像时启动的 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"/>
<data android:mimeType="image/*"/>
</intent-filter>
</activity>
如果某 Activity 想要支持两种不同的隐式 Intent 调用,需要再配置一个 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,您必须在 Intent 过滤器中包含 CATEGORY_DEFAULT 类别。方法 startActivity() 和startActivityForResult() 将按照已声明 CATEGORY_DEFAULT 类别的方式处理所有 Intent。如果您不在 Intent 过滤器中声明它,则没有隐含 Intent 分解为您的 Activity。
处理您的 Activity 中的 Intent
在 onCreate() 中获取传递来的 Intent 信息。
@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 对象来指定代码和结果。
// 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_CANCELED。因此,如果用户在完成操作动作或设置结果之前按了返回按钮,原始 Activity 会收到“已取消”的结果。
如果只需要返回某个整数,可以使用以下方法:
setResult(RESULT_COLOR_RED);
finish();
1.6 Working with System Permissions
请求需要的权限
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
处理权限请求响应
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
在此分享一种权限申请写法:
- 首先定义一个权限申请成功或失败的回调接口:
private interface PermissionCallback
{
void onSuccess();
void onFailure();
}
- 下面代码以申请摄像机权限举例,如果需要申请更多权限除了要在 manifest 文件中添加,也需要在 String[] 字符串数组中添加:
@TargetApi(23)
private void requestCameraPermission(PermissionCallback callback)
{
if (Build.VERSION.SDK_INT >= 23) {
if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
int requestCode = permissionRequestCodeSerial;
permissionRequestCodeSerial += 1;
permissionCallbacks.put(requestCode, callback);
requestPermissions(new String[]{Manifest.permission.CAMERA}, requestCode);
} else {
callback.onSuccess();
}
} else {
callback.onSuccess();
}
}
- 权限申请回调,同时回调自定义接口:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
if (permissionCallbacks.containsKey(requestCode)) {
PermissionCallback callback = permissionCallbacks.get(requestCode);
permissionCallbacks.remove(requestCode);
boolean executed = false;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
executed = true;
callback.onFailure();
}
}
if (!executed) {
callback.onSuccess();
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
- 使用时在相关 Activity 或 app 初始化时调用第 2 步中的方法:
requestCameraPermission(new PermissionCallback() {
@Override
public void onSuccess() {
}
@Override
public void onFailure() {
}
});
网友评论