一. Getting Started
1.5 Interacting with Other Apps
Android 應用一般具有若干個Activity明郭。每個 Activity 顯示一個用戶界面,用戶可通過該界面執(zhí)行特定任務(比如,查看地圖或拍照)。要將用戶從一個 Activity 轉至另一 Activity,您的應用必須使用 Intent 定義您的應用做某事的 “Intent”。
1.5.1 Sending the User to Another App
構建隱式 Intent
- 使用指定電話號碼的 Uri 數據創(chuàng)建發(fā)起電話呼叫的 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);
- 發(fā)送帶附件的電子郵件:
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
- 創(chuàng)建日歷事件:
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
此處顯示完整的示例:如何創(chuàng)建查看地圖的 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
例如,此處顯示如何開始允許用戶選擇聯(lián)系人的 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 對象鉴腻。
本例說明您可以如何處理“選擇聯(lián)系人” 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)
}
}
}
成功獲取聯(lián)系人數據后的進一步操作:
@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...
}
}
}
注意讀取聯(lián)系人需要申請相應權限
1.5.2 Allowing Other Apps to Start Your Activity
要允許其他應用啟動您的 Activity,您需要在清單文件中為對應的 <activity>
元素添加一個 <intent-filter>
元素爽哎。
當您的應用安裝在設備上時蜓席,系統(tǒng)會識別您的 Intent 過濾器并添加信息至所有已安裝應用支持的 Intent 內部目錄。當應用通過隱含 Intent 調用 startActivity()
或 startActivityForResult()
時倦青,系統(tǒng)會找到可以響應該 Intent 的 Activity。
添加 Intent 過濾器
Activity 基本 Intent 過濾器:
- 操作 <action> 標簽:表示要執(zhí)行的操作名盹舞,如 ACTION_SEND 或 ACTION_VIEW产镐。
- 數據 <data> 標簽:與 Intent 關聯(lián)的數據描述√卟剑可以指定 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() {
}
});