Intent 和 Intent 過濾器

Intent是一個(gè)消息傳遞對(duì)象,我們一般用它來在組件間進(jìn)行通信。

一、Intent類型

Intent分為兩種類型:
(1) 顯式Intent
知道要啟動(dòng)的組件確切名稱,通過傳入具體組件名稱來啟動(dòng)組件。 通常啟動(dòng)自己應(yīng)用中的組件都是通過顯式Intent,因?yàn)橐獑?dòng)的 Activity 或服務(wù)的類名我們是知道的。
通常這樣來使用:

Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("key", "value");
startActivity(intent);

(2)隱式Intent
在不確切的知道要打開哪個(gè)組件的情況下,通過指出action、data、category等信息,系統(tǒng)會(huì)尋找到匹配的組件。 例如,如需在地圖上向用戶顯示位置,則可以使用隱式 Intent,請(qǐng)求另一具有此功能的應(yīng)用在地圖上顯示指定的位置。

二、Intent對(duì)象

Intent對(duì)象攜帶了 Android 系統(tǒng)用來確定要啟動(dòng)哪個(gè)組件的信息(例如,準(zhǔn)確的組件名稱或應(yīng)當(dāng)接收該 Intent 的組件類別),以及收件人組件為了正確執(zhí)行操作而使用的信息(例如,要采取的操作以及要處理的數(shù)據(jù))。Intent 中包含的主要信息如下:

  • 組件名稱
      要啟動(dòng)的組件名稱。如需在應(yīng)用中啟動(dòng)特定的組件,則應(yīng)指定該組件的名稱。如下所示,其中SecondActivity.class即為限定的組件名稱,可用Intent構(gòu)造函數(shù)或setComponent()、setClass()、setClassName()設(shè)置組件名稱:
    方法1:在構(gòu)造函數(shù)中傳入Component
Intent intent = new Intent(this,SecondActivity.class);
startActivity(intent);

方法2:設(shè)置Component

Intent intent = new Intent();  
ComponentName componentName = new ComponentName(this, SecondActivity.class);  
// 或者ComponentName componentName = new ComponentName(this, "com.example.myapplication.SecondActivity");  
// 或者ComponentName componentName = new ComponentName(this.getPackageName(), "com.example.myapplication.SecondActivity");  
intent.setComponent(componentName);  
startActivity(intent); 

方法3:設(shè)置Class

Intent intent = new Intent();  
intent.setClass(this, SecondActivity.class);  
// 或者intent.setClassName(this, "com.example.myapplication.SecondActivity");  
// 或者intent.setClassName(this.getPackageName(), "com.example.myapplication.SecondActivity");  
startActivity(intent);
  • Action
      指定要執(zhí)行的通用操作(例如,“查看”或“選取”)的字符串。可通過setAction方法為Intent設(shè)置action,也可在構(gòu)造Intent時(shí)傳入action。
      Android系統(tǒng)預(yù)定義了許多action,這些action代表了一些常見的操作。例如,我們可以使用ACTION_DIAL來調(diào)用撥打電話的頁面,如下所示:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_DIAL);
//intent.setAction("android.intent.action.DIAL");
startActivity(intent);

其中Intent.ACTION_DIAL是Android系統(tǒng)的內(nèi)置常量,值為"android.intent.action.DIAL"。

  • Data
    data可分為兩類:
    (1)uri
    uri由scheme、host、port、path | pathPattern | pathPrefix這4部分組成,Intent的uri可通過setData()方法設(shè)置。
    (2)mimetype
    指定數(shù)據(jù)的 MIME 類型有助于 Android 系統(tǒng)找到接收 Intent 的最佳組件,例如,能夠顯示圖像的 Activity 可能無法播放音頻文件,即便 URI 格式十分類似時(shí)也是如此。mimetype可通過setType()方法設(shè)置。
    注意:若要同時(shí)設(shè)置 URI 和 MIME 類型,需要使用setDataAndType(),而不應(yīng)該調(diào)用 setData()和setType(),因?yàn)樗鼈儠?huì)互相抵消彼此的值
  • Category
      表示一個(gè)包含應(yīng)處理 Intent 組件類型的附加信息的字符串。 一個(gè) Intent 中可以有任意數(shù)量的Category,但大多數(shù)的 Intent 均不需要Category。可以使用addCategory()來指定類別。例如:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_DIAL);
intent.addCategory(Intent.CATEGORY_DEFAULT);//默認(rèn)Category,可省略
startActivity(intent);
  • Extra
    Intent可以攜帶的額外的key-value鍵值對(duì),是其它所有附加信息的集合。使用extras可以為組件提供擴(kuò)展信息,比如,如果要執(zhí)行“發(fā)送電子郵件”這個(gè)動(dòng)作,可以將電子郵件的標(biāo)題、正文等保存在extras里,傳給電子郵件發(fā)送組件。如下所示:
    方法1:通過調(diào)用putExtra()方法設(shè)置數(shù)據(jù),每一個(gè)key對(duì)應(yīng)一個(gè)value數(shù)據(jù)。
// 給someone@domain.com發(fā)郵件發(fā)送內(nèi)容為“Hello”的郵件  
Intent intent =new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL,"someone@domain.com");
intent.putExtra(Intent.EXTRA_SUBJECT,"Subject");
intent.putExtra(Intent.EXTRA_TEXT,"Hello");
intent.setType("text/plain");
startActivity(intent);

目標(biāo)組件可以通過intent.getStringExtra()來讀取郵件內(nèi)容:

Intent intent = getIntent();
String text = intent.getStringExtra(Intent.EXTRA_TEXT);

方法2:通過創(chuàng)建Bundle對(duì)象來存儲(chǔ)所有數(shù)據(jù),然后通過調(diào)用putExtras()方法來設(shè)置數(shù)據(jù)。

Intent intent = new Intent(Intent.ACTION_SEND);
Bundle extra = new Bundle();
extra.putString(Intent.EXTRA_EMAIL,"someone@domain.com");
extra.putString(Intent.EXTRA_SUBJECT,"Subject");
extra.putString(Intent.EXTRA_TEXT,"Hello");
intent.putExtras(extra);
startActivity(intent);

目標(biāo)組件可以通過bundle.getString()來獲得郵件的標(biāo)題和內(nèi)容:

Intent intent = getIntent();
Bundle bundle = intent.getExtras();
String subject = bundle.getString(Intent.EXTRA_SUBJECT);
String text = bundle.getString(Intent.EXTRA_TEXT);
  • Flag
    標(biāo)志可以指示 Android 系統(tǒng)如何啟動(dòng) Activity(例如,Activity 應(yīng)屬于哪個(gè)任務(wù)),以及啟動(dòng)之后如何處理(例如,它是否屬于最近的 Activity 列表),這里涉及到Activity啟動(dòng)的四種模式,例如:
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//相當(dāng)于singleTask
//intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);//相當(dāng)于singleTop 
startActivity(intent);

三、Intent匹配

相比于顯式Intent直接指明組件名稱,隱式Intnet則含蓄了許多,它并不明確指出我們想要啟動(dòng)哪一個(gè)組件,而是指定一系列更為抽象的action和category等信息,然后交由系統(tǒng)去分析這個(gè)Intent,并幫我們找出合適的組件去啟動(dòng)。這時(shí)就需要將intent與清單文件中注冊(cè)的組件的intent-filter進(jìn)行匹配,來幫我們篩選出合適的組件來啟動(dòng)。
  下圖是從網(wǎng)上找的一個(gè)intent-filter的匹配示意圖,從中我們可以看出,只有action、data、category三方都匹配,Intent才算是匹配成功,進(jìn)而才能打開相應(yīng)的組件。

Intent Filter 的匹配過程

一個(gè)組件若在清單文件中聲明了多個(gè)Intent Filter,則只需要匹配任意一個(gè)即可啟動(dòng)該組件。
通過比較解析隱式 Intent 時(shí)主要進(jìn)行以下三層過濾:行為過濾 (action)、數(shù)據(jù)過濾 (data/type)、類別過濾 (category)。
(1)action匹配
   一個(gè)Intent Filter中可聲明多個(gè)action,Intent中的action與其中的任一個(gè)action在字符串形式上完全相同(注意,區(qū)分大小寫),action方面就匹配成功。
注意:隱式Intent必須指定action(如不指定action則必須指定data或mimetype。這種情況下,只要IntentFilter至少含有一個(gè)action就可以匹配)
例如,我們定義如下Intent:

Intent intent = new Intent("android.intent.action.SEND")...

則在清單文件中定義的組件的<intent-filter>中包含名為"android.intent.action.SEND"的action即為匹配成功,如下所示,該組件在action方面就與intent匹配了:

<intent-filter>
 <action android:name="android.intent.action.SEND"/>
 <action android:name="android.intent.action.SEND_TO"/>
</intent-filter>

(2)data/type匹配
  同action類似,只要Intent的data只要與Intent Filter中的任一個(gè)data聲明完全相同,data方面就匹配成功。若Intent Filter的data聲明部分未指定uri,則缺省uri為content或file,Intent中的uri的scheme部分需為content或file才能匹配;
  type屬性用于明確指定data屬性的數(shù)據(jù)類型或MIME類型,但是通常來說,當(dāng)Intent不指定data屬性時(shí),type屬性才會(huì)起作用,否則Android系統(tǒng)將會(huì)根據(jù)data屬性值來分析數(shù)據(jù)的類型,所以無需指定type屬性。
  通過setData方法會(huì)把type屬性設(shè)置為null,設(shè)置setType方法會(huì)把data設(shè)置為null,如果想要兩個(gè)屬性同時(shí)設(shè)置,要使用Intent.setDataAndType()方法。
  如果Intent對(duì)象中既包含Uri又包含Type,那么,在<intent-filter>中也必須二者都包含才能通過測(cè)試。

(3)category匹配
與action和data不同,Intent中的category必須都在Intent Filter中出現(xiàn)才算匹配成功。Intent可以不指定category,若Intent中未指定category,系統(tǒng)會(huì)自動(dòng)為它帶上“android.intent.category.DEFAULT”。所以,想要接收隱式Intent的組件都必須在manifest文件中的Intent Filter聲明中帶上“android.intent.category.DEFAULT”。

三、Intent匹配失敗處理

在上面intent-filter的匹配過程示意圖中我們可以看到,若一個(gè)組件都沒有匹配成功的話,程序就會(huì)出錯(cuò),拋出異常,一般有三種方式可以處理:
(1)使用try-catch捕獲異常
例如我們隨意啟動(dòng)一個(gè)并不存在的Activity--"abcdefg",程序拋出拋出ActivityNotFoundException的異常,可以使用如下try-catch語句來捕獲:

Intent intent = new Intent("abcdefg");  
try  
{  
    startActivity(intent);  
}  
catch(ActivityNotFoundException e)  
{  
    Toast.makeText(this, "找不到對(duì)應(yīng)的Activity", Toast.LENGTH_SHORT).show();  
}  

(2)預(yù)先使用resolveActivity方法判斷
我們可以在startActivity()之前使用PackageManager的resolveActivity或者Intent的resolveActivity方法判斷這個(gè)Intent是否能找到合適的Activity,如果沒有,則不再startActivity,或者可以直接禁用用戶操作的控件。

Intent intent = new Intent("abcdefg");  
if(intent.resolveActivity(getPackageManager()) == null)  
{  
    // 設(shè)置控件不可用  
}  

resolveActivity方法的返回值就是上面講到的ComponentName對(duì)象,一般情況下也就是系統(tǒng)找到的那個(gè)Activity。但是如果有多個(gè)Activity可供選擇的話,則返回的Component是com.android.internal.app.ResolverActivity,也就是用戶選擇Activity的那個(gè)界面對(duì)應(yīng)的Activity:

Intent intent = new Intent(Intent.ACTION_DIAL);  
ComponentName componentName = intent.resolveActivity(getPackageManager());  
if(componentName != null)  
{  
    String className = componentName.getClassName();  
    Toast.makeText(this, className, Toast.LENGTH_SHORT).show();  
}  

(3)預(yù)先使用queryIntentActivities方法判斷
若我們想查看所有匹配的Activity的信息,可以調(diào)用PackageManager的queryIntentActivities,該方法會(huì)返回所有成功匹配Intent的Activity的信息,我們可以根據(jù)該方法返回值判斷是否有匹配的Activity。

public boolean isIntentAvailable(Context context, String action) 
{ 
    final PackageManager packageManager = context.getPackageManager();
    final Intent intent = new Intent(action);
    List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(
                     intent, PackageManager.MATCH_DEFAULT_ONLY);
     if (resolveInfo.size() > 0) 
      {
           return true; 
      }
           return false; 
}

一些常見的通用Intent的使用例子可以查看我的博客:通用Intent

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容