解析Intent,尋找匹配Activity
如果給出component名字(包名、類名)是explicit intent,否則是implicit intent。對於explicit intent,關鍵就是component 名字,在<intent-fliter>中聲明的其他屬性被忽略。對於implicit intent,則根據action,category和data來進行匹配。然而一個intent fliter中可以聲明多個actions,多個categories,多個data屬性,因此可以滿足不同的intent。
對於implicit intent設定了action,則該action必須存在於<intent-fliter>之下的某個<action nandroid:name=”……”>。但是和《Pro Android 4.0》所寫的不一樣,經過測試,我們發現intent-fliter下必須存在action的聲明。
1)對於設定了action的intent,不能匹配intent-fliter下沒有定義action的情況;
2)對於沒有設定action的intent,也不能匹配intent-fliter下沒有定義action的情況;
3)對於沒有設定action的intent,可以匹配intent-fliter下任何定義的action,但是intent應提供足夠的過濾條件,例如data的Uri(測試顯示使用category過濾不能匹配)。
至少在Android 4.2版本下的測試如是。因此,我們應該在inter-fliter中聲明action name,intent應該提供action name,如特殊情況不能提供,需要提供data屬性進行過濾。但這種特殊情況不應該出現,換言之,我們必須做到action匹配。
如果implicit intent設定了data schema,必須能與intent fliter某個data schema匹配;如果intent fliter下設定了data schema,implicit intent必須提供有效data URI,不可為空。對於URI,除了schema外,還可以對authority(即domain)有要求,在manifest中定義為如下。
<data android:scheme="wei" android:host="flowingflying.com"/>
匹配Uri為:wei://flowingflying.com/….,還可以通過android:path,android:pathPrefix,android:pathpattern做進一步限制,具體可以查看http://developer.android.com/guide/topics/manifest/data-element.htm。
同樣,對於data mimetype,也必須雙方進行匹配,不存在不能視為匹配。通過setType()進行設定,如intent.setType(“text/html”);此外,對於subType,可以用*作為通配,例如intent.setType(“text/*”);
ACTION_PICK
Activity A通過intent喚起Activity B,在某種情況下,我們希望B在結束時能換回資訊給A以便A確定下一步的處理工作,這同時也是利用B作為UI的一種方式。一種常用的方式是B是list列表的UI,下面是一個小例子,我們不具體實現B的list UI,只是類比選擇item後設定傳回值和返回資訊,返回資訊由Intent進行攜帶,本例將自動關閉B。
Activity B步驟1:在AndriodManifest.xml中B的intent fliter中增設PICK的action
<activity …… >
<intent-filter>
… …
<data android:scheme="wei" />
<action android:name="android.intent.action.PICK" />
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
Activity B步驟2:在B中設定傳回值和返回資訊,本例返回資訊類比一個NotePad的Uri,並自動關閉B
......
private void testPick(int itemIndex){
//由於intent fliter可以帶有多個action,需要先過濾,只有PICK的action才進行傳回值操作
if(Intent.ACTION_PICK.equals(this.getIntent().getAction())){
//設定傳回值RESULT_OK,並同時通過intent攜帶資訊傳遞,本例通過data傳遞Uri
setResult(Activity.RESULT_OK /*傳回值*/,
new Intent().setData(getNoteUri(itemIndex))
/*攜帶資訊的intent*/);
//自動關閉Activity B,只有B結束時,傳回值才傳遞給A。當然也可以手工結束B,如按系統返回鍵。
finish();
}
}
private Uri getNoteUri(int itemIndex){
return Uri.parse("content://com.google.provider.NotePad/notes/" + itemIndex);
}
Activity A:
private static final int PICK_REQUEST_CODE = 1000;
private void invokePick(Activity activity){
//設定喚起B的intent,action為PICK,本例中的setData只是為implicit的匹配
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setData(Uri.parse("wei://flowingflying.com/notes"));
//不使用startActivity,因為startActivity類似開啟一個modal dialog,無法進行callBack,因此採用startActivityForResult()
activity.startActivityForResult(intent, PICK_REQUEST_CODE /*request code*/);
//由於可以PICK方式喚起多個不同的Activity,所有的callBack都觸發onActivityResult()方法,採用request code用於進行區分。
}
//下面是重寫Activity的onActivityResult()方法,當B結束時被觸發,其中第一個參數requestCode為startActivityForResult()中所攜帶的,可以判斷是從那個Activity中返回;第二個參數是B的傳回值,有系統值RESULT_OK(-1)、RESULT_CANCELED(1),或者使用者自訂值,自訂值必須從RESULT_FIRST_USER(1)開始;第三個參數是B通過intent攜帶的返回資訊
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent outputIntent) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, outputIntent);
//本例只處理Activity B的返回情況,其request code為PICK_REQUEST_CODE){
if(requestCode != PICK_REQUEST_CODE){
Log.d("WEI","Some one else called this. not us");
return;
}
//如果傳回值非OK,即RESULT_CANCELED,不處理
if(resultCode != Activity.RESULT_OK){
Log.d("WEI","Result is not OK");
return;
}
//讀intent攜帶的資訊
Uri uri = outputIntent.getData();
Log.d("WEI","uri is " + uri.toString());
//根據攜帶資訊進行處理:本例B返回一個NotePad的content Uri,用之開啟NotePad。NotePad在模擬器上是沒有的,我們可以通過sample進行匯入,具體參見本博最後的附
startActivity(new Intent(Intent.ACTION_EDIT,uri));
}
ACTION_GET_CONTENT
ACTION_GET_CONTENT和ACTION_PICK非常相似,ACTION_PICK是給出URI,系統給出一個或者多個匹配的選擇給使用者,執行後,喚起的Activity傳回值給喚起方。 而ACTION_CET_CONTENT不是基於URI,而是根據MIME Type。
本例使用NotePad,請參考本博最後的附:為模擬器安裝NotePad。我們先看看sample NotePad的Manifest.xml檔案。該檔案展現了多個intent-fliter共存的情況,分別針對應用啟動,PICK喚起和GET_CONTENT喚起三種情況。
<activity android:name="NotesList" android:label="@string/title_notes_list">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.EDIT" />
<action android:name="android.intent.action.PICK" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.GET_CONTENT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
</intent-filter>
</activity>
代碼編寫和PICK相似:
private static final int REQUEST_CODE_GET_CONTENT = 101; private void invokeGetContent(Activity activity){ Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("vnd.android.cursor.item/vnd.google.note"); //GET_CONTENT針對MIME TYPE
activity.startActivityForResult(intent, REQUEST_CODE_GET_CONTENT);
} @Override //接收Acivity的返回資訊 protected void onActivityResult(int requestCode, int resultCode, Intent outputIntent) {
if(resultCode != Activity.RESULT_OK){ Log.d("Wei","Result is not OK"); return; } switch(requestCode){ case REQUEST_CODE_PICK: …… //對ACTION_PICK的處理,參考上一例子 break; case REQUEST_CODE_GET_CONTENT: Uri uriContent = outputIntent.getData(); Log.d("Wei","uri is " + uriContent.toString());
startActivity(new Intent(Intent.ACTION_EDIT,uriContent)); break; default: showInfo("Some one else called this. not us"); break; } } |
Pending Intent
Intent可以作為PendingIntent中的一個參數。PengdingIntent,顧名思義,不是馬上調用的,可以在某個事件觸發後才喚起。例如在通知中使用,得到使用者點擊通知時才調起,見Android學習筆記(五四):通知Notification(上);又例如警告,如地理位置匹配時調起,見Android學習筆記(五六):位置Location。即時喚起方進程(應用)已經不存在,PendingIntent仍得以很好地儲存,很方便進程結束後或其他進程調用。
我們做個小實驗,看看PendingIntent如何建立:
public static PendingIntent getActivity (Context context, int requestCode, Intent intent, int flags)
public static PendingIntent getActivities (Context context, int requestCode, Intent[] intents, int flags)
Intent intent = new Intent(this,IntentBasicViewActivity.class);
//普通的intent
PendingIntent pi = PendingIntent.getActivity(getApplicationContext(),0,intent,0);
showInfo("intent is " + intent);
//showInfo()同時在LogCat和UI視窗中顯示
showInfo("pending intent is " + pi);
PendingIntent pi1 = PendingIntent.getActivity(getApplicationContext(),0,intent,0);
showInfo("pending intent is " + pi1);
PendingIntent pi2 = PendingIntent.getActivity(getApplicationContext(),1,intent,0);
showInfo("pending intent is " + pi2);
為何使用getActivity如此奇特的方式。普通的intent可以通過startActivity(intent),startService(intent)和sendBroadcast(intent)來喚起Activity,開啟服務和喚起broadcast receiver。由於最終要通過PendingIntent中的intent參數實際去喚起對應的Activity、服務或廣播接收器,需要告之系統到底是Activity、服務還是廣播。相應的也會有:
getBroadcast(Context context, int requestCode, Intent intent, int flags)
getService(Context context, int requestCode, Intent intent, int flags)
至於為何用getxxxx這種方式,據《Pro Android 4.0》是這樣解釋:Android會儲存PendingIntent,並可以多次重用(具體取決於flag參數的設定),如果需要使用相同的intent,需要獲得相同的PendingIntent,所有用get。如果要進行區分,則可設定不同的requestCode。
對於flag參數,有:
【1】FLAG_CANCEL_CURRENT:如果當前系統中已經存在一個相同的PendingIntent對象,那麼就將先將已有的PendingIntent取消,然後重建一個PendingIntent對象。
【2】FLAG_NO_CREATE:如果當前系統中不存在相同的PendingIntent對象,系統將不會建立該PendingIntent對象而是直接返回null。
【3】FLAG_ONE_SHOT:該PendingIntent只作用一次。在該PendingIntent對象通過send()方法觸發過後,PendingIntent將自動調用cancel()進行銷毀,那麼如果你再調用send()方法的話,系統將會返回一個SendIntentException。
【4】FLAG_UPDATE_CURRENT:如果系統中有一個和你描述的PendingIntent對等的PendingInent,那麼系統將使用該PendingIntent對象,但是會使用新的Intent來更新之前PendingIntent中的Intent對象資料,例如更新Intent中的Extras。
【說明】這是其中幾個flag的解釋,參考自http://blog.csdn.net/hudashi/article/details/7060837,更多的請閱讀http://developer.android.com/reference/android/app/PendingIntent.html#FLAG_CANCEL_CURRENT
如果我們獲得一個PendingIntent,要喚起當中的Intent,可以使用
pi.send(RESULT_CODE); //PendingInten多個send方法
附:為模擬器安裝NotePad
從Android SDK Manager上下載sample代碼,然後根據的操作將之加入模擬器。
為了在PICK和GET_CONTENT調用中有更好的使用者體驗,當使用者選擇了item後,NotePad activity能夠自動關閉。我們在NotesList.java中加入以下一行代碼:
protected void onListItemClick(ListView l, View v, int position, long id) {
……
// Handles requests for note data
if (Intent.ACTION_PICK.equals(action) || Intent.ACTION_GET_CONTENT.equals(action)) {
// Sets the result to return to the component that called this Activity.
// result contains the new URI
setResult(RESULT_OK, new Intent().setData(uri));
//Wei Add the following: close NoteList automatically
finish(); //wei Add
……
}
相關連結:
我的Android開發相關文章