Pro Android學習筆記(十二) 瞭解Intent(下)
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
解析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做進一步限制,具體可以檢視
同樣,對於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開發相關文章