Android學習 開發文檔(Training)06 app間交互

interacting with other Apps

爲了實現activity之間的交互,我們必須使用Intent。當我們使用startActivity()來向系統傳遞一個intent的時候,系統會用intent來定位和開啓一個合適的activity。

intent可以明確指定一個activity的實例,也可以是模糊的比如拍照。

1. sending the user to another app

當你想要是一個獨立的app執行一個動作,就好比開啓一個地圖,你必須使用模糊intent。

1.1building an implicit intent

模糊intent不會聲明將要開始的組建類名,而是聲明一個將要執行的動作。動作會說明你想要執行的操作。intent也會包含和動作相關的數據,比如uri數據。

如果數據是uri,有一個簡單的intent構造函數還定義動作和數據。

Uri number = Uri.parse("tel:5551234");
Intent callIntent = new Intent(Intent.ACTION_DIAL, number);

當你的app調用startActivity()的時候,手機就會用你給的電話號碼撥打。

Here are a couple other intents and their action and Uri data pairs:

  • view a map
// 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);
  • view a web page:
Uri webpage = Uri.parse("http://www.android.com");
Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);

其他類型的模糊intent需要額外不同類型的數據,比如一個字符串。可以使用putExtra()來添加一個或者多個。

默認情況下,基於uri系統會決定合適的MIME類型來供intent使用。If you don’t include a Uri in the intent, you should usually use setType() to specify the type of data associated with the intent. Setting the MIME type further specifies which kinds of activities should receive the intent.

  • send an email with an 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[] {"[email protected]"}); // 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
  • create a calendar event:
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");

1.2 verify there is an app to receive the intent

爲了保證app不奔潰,我們必須在啓動前驗證是不是存在app來接受我們的intent。調用 queryIntentActivities() 來得到可用app的列表。如果列表沒有空說明存在。

PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent,
        PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;

1.3 start a activity with the intent

如果你已經創建了你的intent並且設置了外部的參數,調用startActivity()。如果有多個app可以執行這個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);
}

1.3 show a app chooser

如果存在超過一個的app可以執行你的intent,你可以選擇一個默認的打開程序
。但如果是分享類的,你可能需要多個app來執行這個操作。爲了創建這麼一個選擇的對話框,你可以使用createChooser()來創建一個intent。

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);
}

2. getting a result from an activity

你也可以開啓一個activity,然後得到一個結果。調用startActivityForResult()
比如,你可以打開照相機app,然後接收到拍攝的照片。當然,返回值的activity必須被設計爲return一個值。執行完畢後,它發送另一個app作爲結果,你的ativity通過onActivityResult()接收。

2.1 start the activity

這個時候的intent和之前的intent其實沒有什麼特殊的,但是startActivityForResult()需要一個額外的int參數。這個integer參數是一個request code,定義了你的要求。當你接受結果intent的時候,回調函數會提供相同的要求代碼,方便你的app可以很好的識別結果和處理。

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);
}

2.2 receieve the result

返回結果的時候,系統會調用onActivityResult()方法。這個方法包含三個參數

  • The request code you passed to startActivityForResult().
  • A result code specified by the second activity. This is either
    RESULT_OK if the operation was successful or RESULT_CANCELED if the
    user backed out or the operation failed for some reason.
  • An Intent that carries the result data.
@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)
        }
    }
}

爲了很好的處理結果,必須瞭解結果intent的格式。如果是自己的app就很容易得知,如果是系統平臺的app,系統會給出他們自己的接口。For instance, the People app always returns a result with the content URI that identifies the selected contact, and the Camera app returns a Bitmap in the “data” extra (see the class about Capturing Photos).

Bonus: Read the contact data:

@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...
        }
    }
}

3. allowing other apps to start your activity

之前我們都是通過我們的app來啓動其他的app,這裏我們開始學習如何從其他的app通過action來啓動我們自己的app。我們需要在manifest文件中爲<activity>添加<intent-filter>元素。

在你的app安裝之後,系統會識別你的intent filter並且將信息添加到內部所有intent的分類中。當調用startActivity() or startActivityForResult()的時候,如果是一個模糊的intent,那麼尋找這個可以執行intent的相關app。

3.1 add an intent filter

intent filter應該根據活動的類型和activity接受的數據儘可能準確。

  • action
    定義要執行的操作的一個字符串,通常使用平臺定義好的值類似於ACTION_SEND
  • data
    對於數據的一個描述,在intent filter中使用元素。you can specify just the MIME type, just a URI prefix, just a URI scheme, or a combination of these and others that indicate the data type accepted.
  • category
    提供一個附加的方式來標識處理intent的activity,通常和用戶的手勢或者定位有關係
    。系統中有幾種不同的類別,但是很多是幾乎不怎麼用到的。所有的模糊intent都是默認CATEGORY_DEFAULT 定義。

For example, here’s an activity with an intent filter that handles the ACTION_SEND intent when the data type is either text or an image:

<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-filter>只能有一個action和數據類型。但是可以在多個<intent-filter>中定義多個。

如果兩個操作的數據和action是互斥的,我們需要創建兩個<intent-filter>

例如,假設您的活動同時處理ACTION_SEND和ACTION_SENDTO意圖的文本和圖像。在這種情況下,您必須爲兩個操作定義兩個獨立的intent過濾器,因爲ACTION_SENDTO intent必須使用數據Uri來指定收件人的地址,使用send或sendto Uri方案。例如:

<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,必須設置category爲 CATEGORY_DEFAULT,不然不會有反應。

3.2 handle the intent in your activity

爲了知道activity需要執行什麼操作,可以讀取intent。啓動你的app後,你可以通過getIntent()來得到啓動app的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 ...
    }
}

3.3 return a result

調用setResult()來指明結果代碼和intent。並且可以通過finish()來關閉activity。

// 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_CANCEL,並且默認是cancel。必要的時候可以在intent中添加數據。

如果你只是想要返回一個整形數據,那麼你可以直接設置結果代碼爲你的大於0的任何值。這樣就可以不用包括intent了。

setResult(RESULT_COLOR_RED);
finish();

沒有必要檢查activity是否被startActivity()或者startActivityForResult()得到的。都運行setResult()。如果要返回,那麼會自動執行,如果不用返回就會自動忽略。

至此,結束。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章