Pro Android學習筆記(十二):瞭解Intent(下)

來源:互聯網
上載者:User

解析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開發相關文章

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.