讓你的Android應用與外部元素互動起來

傳送門 ☞ 輪子的專欄 ☞ 轉載請註明 ☞ http://blog.csdn.net/leverage_1229

        一個Android應用程序通常有幾個activities。每個act顯示一個用戶接口允許用戶執行一個指定的任務。用戶從一個act到另一個act,你的App必須使用一個Intent對象來定義你App想做些什麼事。當你通過一個Intent調用startActivity()方法時,系統會使用Intent來鑑定和啓動合適的App組件。一個Intent可以明確的啓動一個特定的組件(如一個特定的act實例)或隱式啓動任何可以處理預定動作的組件,本章我們將講述怎麼使用Intent來執行與其他Apps的一些交互,例如啓動另一個App,從那個App接收結果。並使你的應用程序能夠響應來自其他App的intents。

1發送用戶到另一個App

        Android最重要的一個特性之一就是發送用戶到另一個App並基於“動作”來執行它的能力。例如,如果你的app有商業地址想要顯示在地圖上,你不得不在你的App中新建一個activity來顯示地圖。其實更好的辦法是使用Intent發送一個查看地圖的外部請求。Android系統會啓動那個能查看地址的App。通常,我們使用一個明確的Intent,它定義的明確的類名。然而,當你想有一個單獨的應用程序執行時,如“查看地圖”,你必須使用一個隱式的Intent。本節講述如何爲一個特定的動作創建隱含的意圖,以及怎樣用它來啓動執行另一個應用程序中的Activity。

1.1構建一個隱式的Intent

        隱式intents不用申明啓動組件的類名,但需要申明執行的動作。動作指定你想要做的事情,如view(查看),edit(編輯),send(發送)或獲得某事物。Intents經常包託一些與動作相關聯的稅局,如你想要查看的地址,或者你想要發送的email信息。這取決於你想要創建的Intent所發送的數據,數據可能是一個Uri或intent根本不需要數據也能發送。
使用Uri數據撥打電話:
Uri number = Uri.parse("tel:5551234");
Intent callIntent = new Intent(Intent.ACTION_DIAL, number);
        當你的app通過startActivity()調用intent時,電話app根據給定的電話號碼發起呼叫。
查看一個地圖:
// 基於地址的地圖點
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
// 或基於經緯度的地圖點
// Uri location = Uri.parse("geo:37.422219,-122.08364?z=14"); // z參數表示縮放級別
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
查看一個web頁面:
Uri webpage = Uri.parse("http://www.android.com");
Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);
        其他種類的隱式intents需要”extra”數據來提供不同的數據類型,如一個字符串。你能使用putExtra()方法來添加一個或更多extra 數據。默認的,系統通過基於Uri的intent來確定適當的MIME(Multipurpose Internet Mail Extensions)類型。如果你在intent中不包含一個Uri,你應該使用setType()來指定intent相關聯的數據類型。設置MIME類型來進一步指定activities將要接收的intent類型。
發送帶有附件的電子郵件:
Intent emailIntent = new Intent(Intent.ACTION_SEND);
//沒有Uri的intent,所以需要聲明”text/plain”的MIME類型
emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"[email protected]"}); // 收件人
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"));
創建一個日曆事件:
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");
注意: 對於日曆事件的intent只在API Level或更高版本下才支持。

1.2驗證一個App接收的Intent

        我們總是應該在調用一個intent之前先包含一個驗證。這是一個好的習慣,因爲如果你在app中調用intent後,如果沒有可用的設備,那麼你的app會崩潰.爲了驗證那個activity可用,我們可以調用queryIntentActivities()來獲得一個list,如果返回的List不爲空,那麼你能安全的使用intent。
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
boolean isIntentSafe = activities.size() > 0;
        如isIntentSafe爲true,那麼表示至少有一個app能響應我們的intent。如果false,表示沒有一個app能處理這個intent。

1.3使用Intent啓動一個Activity

        你可以創建intent並設置extra的信息,然後調用startActivity()。如果系統識別有多個activity能處理這個intent,那麼它會顯示一個對話框讓你自主選擇。如果只有一個activity的話,系統會立即啓動這個activity。
        讓我們看看如下的代碼,看它是怎麼啓動activity的:
// 構建intent
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
// 驗證上面的mapIntent
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0);
boolean isIntentSafe = activities.size() > 0;
// 如果它是安全的就啓動這個activity
if (isIntentSafe) {
    startActivity(mapIntent);
}

1.4顯示APP選擇器

        注意當你通過intent使用startActivity()啓動activity時,如果有多個app響應,如果多個應用可以處理我們的操作,並且用戶可能想要每次啓動不同的app,比如一個”分享”的動作,分享的渠道可能有多個app組成,這樣用戶每次可能使用不同的app。那麼我們可以使用createChooser()來創建顯示選擇器。
Intent intent = new Intent(Intent.ACTION_SEND);
...
// 用於標題的文本資源例如"Share this photo with"
String title = getResources().getText(R.string.chooser_title);
// 創建並啓動chooser
Intent chooser = Intent.createChooser(intent, title);
startActivity(chooser);

2從Activity獲得結果

        啓動另一個activity並不一定是單向的。我們還可以啓動另一個activity,並接收返回結果.。如果需要接收返回結果,我們可以使用startActivityForResult()。L例如。你的應用啓動一個攝像機app並接收拍攝照片的結果。當然,獲得響應的activity必須被設計爲返回一個結果,當它這麼做時,它發送一個intent對象的結果。你的activity在onActivityResult()回調方法中會接收到這個intent。雖然我們可以使用明確的和隱式的intent,但這裏實際建議你應該使用一個明確的intent,以確保收到了預期的結果。

2.1啓動Activity

        使用startActivityForResult()方法,需要傳遞一個額外的int參數。Int參數的意思爲”request code”,就是標識一個請求。當收到intent結果時,回調提供了相同的請求的代碼,使應用程序可以正確識別結果,並決定如何處理它。
如何啓動一個activity並允許用戶選擇一個聯繫人:
static final int PICK_CONTACT_REQUEST = 1;  // request code
...
private void pickContact() {
    Intent pickContactIntent = new Intent(Intent.ACTION_PICK, new Uri("content://contacts"));
    pickContactIntent.setType(Phone.CONTENT_TYPE); 
    startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST);
}

2.2接收一個結果

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // 檢查響應的請求
    if (requestCode == PICK_CONTACT_REQUEST) {
        // 確保請求是成功的
        if (resultCode == RESULT_OK) {
            // Do something...
        }
    }
}
        爲了成功地處理結果,你必須明白,intent結果的格式將是什麼。例如,People app(早些版本叫Contacts app)始終用content URI返回結果並識別被選擇的聯繫人,Camera app在返回一個Bitmap。
將上面的代碼擴展一下,讓我們看下怎樣讀取聯繫人數據:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    //檢查響應的請求
    if (requestCode == PICK_CONTACT_REQUEST) {
        //確保請求是成功的
        if (resultCode == RESULT_OK) {
            // 獲得指向選定聯繫人的URI
            Uri contactUri = data.getData();
            //我們只需要號碼(NUMBER)列 
            String[] projection = {Phone.NUMBER};
 
            // 在聯繫人中執行查詢,獲得NUMBER column
            // 我們不需要挑選或排序
            // 提示:  query()方法應該在單獨的線程執行,避免阻塞UI線程
            // 考慮使用CursorLoader 執行查詢
            Cursor cursor = getContentResolver()
                    .query(contactUri, projection, null, null, null);
            cursor.moveToFirst();
 
            // 從NUMBER column中檢索電話號碼
            int column = cursor.getColumnIndex(Phone.NUMBER);
            String number = cursor.getString(column);
 
            // 使用電話號碼做些事情
        }
    }
}
注意:Android 2.3 (API level 9)以前,在Contacts Provider執行一個查詢需要申明READ_CONTACTS權限,雖然在2.3開始有一個臨時的權限可以讓你去讀取Contacts Provider,但依舊不能查詢。所以不管什麼版本我們都申明READ_CONTACTS權限即可。

3允許其他Apps啓動你的Activity

        在android平臺上,如果你要集成facebook的社交功能,那麼你可以使用facebokk提供的一個apk,裏面集成了facebook的衆多功能,如分享信息照片等。在實際開發過程中,可能我們需要開發這樣的一個類似的apk,別擔心,android提供這樣的功能並且很容易實現。

3.1添加一個Intent Filter

        我們需要正確的定義intents,讓activity能更好的處理。每一個intent filter應該添加具體的action類型和數據類型。系統可能會發送一個給定的intent到一個activity,如果activity有一個intent filter並符合下列條件的intent對象:
Action
        一個用來執行動作的字符串名字。例如ACTION_SEND或ACTION_VIEW。在intent filter中的<action>節點指定它。必須是全稱,不能使用API常量。
Data
        相關的intent中數據的描述。在intent filter中的<data>節點指定它。在這個節點中使用一個或多個屬性,你能指定MIME類型,一個URI前綴,一個URI組合,或者是以上內容的組合。如果你不需要申明指定的Uri數據,那你僅指定 android:mimeType屬性即可,例如text/plain或image/jpeg。
Category
        提供一種額外的方法來表示activity處理的intent,通常與用戶手勢和開始位置相關。系統支持幾種不同的類別,但大部分都很少使用,一般在intent filter中的<category>節點使用CATEGORY_DEFAULT。
下面的<intent-filter>中定義的內容表示在處理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只有一個動作和一種數據類型,但聲明多個<data>,<action>,<category>也是OK的,如果動作和數據類型相互排斥的話,你就應該分開它們。加入你的activity處理文本和圖像,並且使用ACTION_SEND和ACTION_SENDTO intents。這樣就是錯誤的,在這種情況下你應該使用兩個<intent-filter>來分開它們。因爲SENDTO必須使用Uri數據,並且需要sms和smsto的scheme。
<activity android:name="ShareActivity">
    <!—爲發送文本過濾; 接收SENDTO action 使用 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>
    <!—爲發送文本或圖像過濾; 接收SEND action和文本或者圖像數據 -->
    <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>
        爲了接收隱式的intents,我們必須在<intent-filter>中定義CATEGORY_DEFAULT中,如果沒聲明,那麼你的activity不能解決處理隱式的intents。

3.2 在Activity中處理intent

        爲了決定在你activity中想要處理的動作,你能在啓動activity時讀取intent。在啓動activity時,調用getIntent()方法來檢索啓動activity的intent。你能在任意時刻這麼做,但最好在onCreate()或onStart()中這樣做。
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    //獲得啓動activity的intent
    Intent intent = getIntent();
    Uri data = intent.getData();
 
    // 解決intent類型想做什麼
    if (intent.getType().indexOf("image/") != -1) {
        //處理圖像數據
    } else if (intent.getType().equals("text/plain")) {
        // 處理文本
    }
}

3.3 返回一個結果

        如果你想要返回一個結果到你調用的activity,最簡單的就是調用setResult()方法來指定結果代碼和intent結果。當你的操作完成時,用戶如果想要返回到最開始的activity,調用finish()來關閉和destroy你的activity即可。
// 創建intent來傳遞某種結果數據
Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri");
setResult(Activity.RESULT_OK, result);
finish();
        我們可以只指定result code。通常是RESULT_OK或RESULT_CANCELED。你可以添加額外的intent或者不添加。默認的result code是RESULT_CANCELED。所以如果用戶在你設置result之前執行Back鍵,那麼最初的activity收到的就是RESULT_CANCELED,這並不是我們預計的結果,這個細節請注意。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章