什么是Intent
intent是一個消息傳遞對象,可以使用它從其他應用組件請求操作。通常我們用intent來啟動一個activity,服務或者是廣播,
啟動activity
? ? 使用startactivity() 來啟動一個activity。intent則封裝了要啟動的activity的信息以及傳遞的數據。如果你希望在activity完成后收到結果,你需要調用startactivityforresult()方法,在activity的onactivityresult()回調中的intent返回了數據內容
啟動服務
? ? service是一個后臺執行操作的組件,他沒有用戶界面,通過startservice打開一個服務
傳遞廣播
? ? 廣播是任何應用都可以接受的消息,通過sendbroadcast()傳遞廣播
就拿發送email舉例吧,發送email必要條件:發送者,接受者,和載體(就是email本身)。intent類似于email,他作為一個傳遞對象封裝了要傳遞的信息和內容,再由發送者(調用startactivity的界面)發送給接收者。
?Intent 過濾器
? ? intent過濾器是應用的清單文件中的一個表達式,他制定該組件要接收的intent類型,比如:通過給activity聲明intent過濾器,可以使其他應用能夠直接使用某一特定類型的intent來啟動activity,如果沒有給activity聲明過濾器就不能通過隱式意圖來啟動,只能通過顯式意圖來啟動activity
Intent的類型
intent的類型的類型有兩種,一種叫顯式意圖,一種叫隱式意圖
顯式意圖:通常在自己的程序中使用,明確知道我們要做什么事情,啟動什么組件。
隱式意圖:通常是調用第三方程序執行,我們不知道組件的名稱,但是知道要執行的操作,制定匹配規則,讓滿足規則的程序來執行。
創建隱式意圖時,android系統通過將intent的內容與設備上其他應用清單文件聲明的intent過濾器進行比較,從而找到要啟動的對應的組件,如果intent與ent過濾器匹配,則系統降啟動這個組件,同事傳遞intent對象給組件, 如果有多個intent過濾器相匹配則系統會顯示一個列表對話框,顯示所有匹配的組件供用戶自行選擇要使用的應用。
創建一個顯式意圖
Intent intent =new Intent();? 創建意圖對象
intent.setClass(c, ScanActivity.class);? 設置要打開的目標
intent.putExtra("title", title);? 給意圖填充數據
intent.putExtra("hint", hint);
intent.putExtra("typeName", typeName);
activity.startActivity(intent);? 開啟
創建一個隱式意圖intent
創建隱式意圖要注意的是,如果手機沒有任何應用來處理你的隱式意圖,則調用失敗應用會崩掉,所以在這之前要調用intent.resolveActivity()方法判斷是否有應用匹配規則,如果返回非空,則至少有一個應用能夠處理該意圖,則可以調用,否則不應該調用
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND); 表明你要做什么事情
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage); 目標需要的內容
sendIntent.setType("text/plain"); 數據類型
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);}
接收隱式意圖
? ? 要想應用能夠接收隱式意圖,必須在清單文件中使用<intent-filter>元素為組件聲明過濾器。<intent-filter>元素應該嵌入對應的組件中,通常為activity。我們可以為組件設置一個或者多個過濾器,每個過濾器均需要根據意圖操作,數據和類別指定自身接收的類型,當隱式意圖滿足過濾器規則時,系統就會將該意圖傳遞給應用組件。
<intent-filter>元素的子元素
? ? <action>
在那么屬性中,聲明接受的意圖操作,鈣質必須為操作的文本字符串值,而不是常量類。
<action android:name="android.intent.action.SEND"> name屬性的值可以自定義,也可以使用安卓系統提供的一些默認值
? ? <data>
使用一個或者多個指定數據URI各個方面(scheme,host,port,path等)和MIME類型的屬性,聲明接受的數據類型
<data android:mimeType="text/plain"/>
每個data分為MIME?和scheme。scheme包含host,port,path等。
MIME (Multipurpose?Internet?Mail?Extensions) 是描述消息內容類型的因特網標準。
? ? <category>
在name屬性中,聲明接受的意圖類別,鈣質必須是操作文本字符串值,而不是常量類。
<category android:name="android.intent.category.DEFAULT>
為了接受隱式意圖,必須將 category.DEFAULT 類別包括在過濾器中,如果沒有在過濾器聲明此類別,則隱式意圖不會解析該組件
Intent Filter匹配規則
Intent解析機制主要是通過查找已注冊在AndroidManifest.xml中的所有IntentFilter及其中定義的Intent,最終找到匹配的Intent。在這個解析過程中,Android是通過Intent的action、type、category這三個屬性來進行匹配判斷的。一個過濾列表中的action、type、category可以有多個,所有的action、type、category分別構成不同類別,同一類別信息共同約束當前類別的匹配過程。只有一個Intent同時匹配action、type、category這三個類別才算完全匹配,只有完全匹配才能啟動Activity。另外一個組件若聲明了多個Intent Filter,只需要匹配任意一個即可啟動該組件。?
? ? ? ?action是一個字符串,如果Intent指明定了action,則目標組件的IntentFilter的action列表中就必須包含有這個action,否則不能匹配。一個Intent Filter中可聲明多個action,Intent中的action與其中的任一個action在字符串形式上完全相同(注意,區分大小寫,大小寫不同但字符串內容相同也會造成匹配失敗),action方面就匹配成功。可通過setAction方法為Intent設置action,也可在構造Intent時傳入action。需要注意的是,隱式Intent必須指定action。比如我們在Manifest文件中為MyActivity定義了如下Intent Filter:
<intent-filter>
? ? ? <action android:name="android.intent.action.SEND"/>
????<action android:name="android.intent.action.SEND_TO"/>
</intent-filter>
? ? ? ?那么只要Intent的action為“SEND”或“SEND_TO”,那么這個Intent在action方面就能和上面那個Activity匹配成功。比如我們的Intent定義如下:
Intent intent =newIntent("android.intent.action.SEND") ;startActivity(intent);
那么我們的Intent在action方面就與MyActivity匹配了。?
Android系統預定義了許多action,這些action代表了一些常見的操作。常見action如下(Intent類中的常量):
Intent.ACTION_VIEW
Intent.ACTION_DIAL
Intent.ACTION_SENDTO
Intent.ACTION_SEND
Intent.ACTION_WEB_SEARCH
總結起來有兩點結論:?
1. 要想讓intent對象通過action測試,那么intent-filter中聲明的action不能為空且要包含intent對象中的action值(如果intent的action值不為空的話)。?
2. 如果intent-filter沒有聲明任何action,那么所有的intent的對象(即無論intent如何配置)都無法通過intent-filter的action測試。
如果Intent沒有提供type,系統將從data中得到數據類型。和action一樣,同action類似,只要Intent的data只要與Intent Filter中的任一個data聲明完全相同,data方面就完全匹配成功。?
data由兩部分組成:mimeType和URI?
MineType指的是媒體類型:例如imgage/jpeg,auto/mpeg4和viedo/*等,可以表示圖片、文本、視頻等不同的媒體格式?
uri則由scheme、host、port、path | pathPattern | pathPrefix這4部分組成
其中scheme既可以是Android中常見的協議,也可以是我們自定義的協議。Android中常見的協議包括content協議、http協議、file協議等,自定義協議可以使用自定義的字符串。
如下是一個content協議的URI:
content://com.example.project:200/folder/subfolder/etc?
在該URI中,scheme是content,host是com.example.project,port是200,path是folder/subfolder/etc。
如下是一個自定義協議的URI:
ispring://blog.csdn.net/sunqunsunqun?
在該URI中,scheme是ispring,host是blog.csdn.net,沒有明確設定port,path是sunqunsunqun。
組成URI的這些屬性在標簽中都是可選的 ,但存在如下的依賴關系:
如果沒有指定scheme,那么host參數會被忽略
如果沒有指定host,那么port參數會被忽略
如果scheme和host都沒有指定,path參數會被忽略
當我們將intent對象中的Uri參數與intent-filter中的標簽指定的URI格式進行對別時,我們我們只對比intent-filter的標簽指定的部分,例如:
如果intent-filter中只指定了scheme,那么所有帶有該sheme的URI都能匹配到該intent-filter。
如果intent-filter中只指定了scheme和authority(authority包括host和port兩部分)而沒有指定path,那么所有具有相同scheme和authority的URI都能匹配到該intent-filter,而不用考慮path為何值。
如果intent-filter中同時指定了scheme、authority和path,那么只有具有相同scheme、authority和path的URI才能匹配到該intent-filter。
需要注意的是,intent-filter的標簽在指定path的值時,可以在里面使用通配符*,起到部分匹配的效果。
data測試需要同時將intent對象中的URI、MIME類型與intent-filter的標簽中指定的URI、MIME類型進行對比。?
我們知道一個intent-filter下可以有多個標簽,intent對象無需通過所有的標簽測試,一般情況下,我們的intent對象只需通過了其中一個標簽的測試并滿足某些特定情形下的一些條件,那么該intent對象就通過了該intent-filter的data測試。?
Intent的uri可通過setData方法設置,mimetype可通過setType方法設置。?
需要注意的是:若Intent Filter的data聲明部分未指定uri,則缺省uri為content或file,Intent中的uri的scheme部分需為content或file才能匹配;若要為Intent指定完整的data,必須用setDataAndType方法,究其原因在,setData和setType方法的源碼中我們發現:
publicIntentsetData(Uri data) {?
?? mData = data;?
?? mType =null;
????returnthis;
}
publicIntent setType(Stringtype) {
mData =null;? ?
?mType =type;
returnthis;
}
這兩個方法會彼此互相清除對方的值(這個比較逗),即setData會把mimeType置為null,setType會把uri置為null。?
下面我們來舉例說明一下data的匹配。首先我們先來看一下Intent Filter中指定data的語法:
<data android:scheme="String.“
? ? ? ? ? android:host="String"
? ? ? ? ? android:port="String"
? ? ? ? ? android:path="String"
? ? ? ? ? android:pathPattern="String"
? ? ? ? ? android:pathPrefix="String"
? ? ? ? ? android:mimeType="String"/>
? ? 其中scheme、host等各個部分無需全部指定。
使用案例:?
(1)如果我們想要匹配 http 以 “.pdf” 結尾的路徑,使得別的程序想要打開網絡 pdf 時,用戶能夠可以選擇我們的程序進行下載查看。?
我們可以將 scheme 設置為 “http”,pathPattern 設置為 “.*//.pdf”,整個 intent-filter 設置為:
<intent-filter>
<actionandroid:name="android.intent.action.VIEW">
<categoryandroid:name="android.intent.category.DEFAULT">
<dataandroid:scheme="http"android:pathPattern=".*//.pdf">
</intent-filter>
如果你只想處理某個站點的 pdf,那么在 data 標簽里增加 android:host=”yoursite.com” 則只會匹配?http://yoursite.com/xxx/xxx.pdf,但這不會匹配 www.yoursite.com,如果你也想匹配這個站點的話,你就需要再添加一個 data 標簽,除了 android:host 改為 “www.yoursite.com” 其他都一樣。
(2)如果我們做的是一個IM應用,或是其他類似于微博之類的應用,如何讓別人通過 Intent 進行調用出現在選擇框里呢?我們只用注冊 android.intent.action.SEND 與 mimeType 為 “text/plain” 或 “/” 就可以了,整個 intent-filter 設置為:
<intent-filter>
<actionandroid:name="android.intent.action.SEND"/>
<categoryandroid:name="android.intent.category.DEFAULT"/>
<datamimeType="*/*"/>
</intent-filter>
這里設置 category 的原因是,創建的 Intent 的實例默認 category 就包含了 Intent.CATEGORY_DEFAULT ,google 這樣做的原因是為了讓這個 Intent 始終有一個 category。
(3)如果我們做的是一個音樂播放軟件,當文件瀏覽器打開某音樂文件的時候,使我們的應用能夠出現在選擇框里?這類似于文件關聯了,其實做起來跟上面一樣,也很簡單,我們只用注冊 android.intent.action.VIEW 與 mimeType 為 “audio/*” 就可以了,整個 intent-filter 設置為:
<intent-filter>
<actionandroid:name="android.intent.action.VIEW"/>
<categoryandroid:name="android.intent.category.DEFAULT"/>
<dataandroid:mimeType="audio/*"/>
</intent-filter>
? ? ? ?category也是一個字符串,但是它與action的過濾規則不同,它要求Intent中個如果含有category,那么所有的category都必須和過濾規則中的其中一個category相同。也就是說,Intent中如果出現了category,不管有幾個category,對于每個category來說,它必須是過濾規則中的定義了的category。當然,Intent中也可以沒有category(若Intent中未指定category,系統會自動為它帶上“android.intent.category.DEFAULT”),如果沒有,仍然可以匹配成功。category和action的區別在于,action要求Intent中必須有一個action且必須和過濾規則中的某幾個action相同,而category要求Intent可以沒有category,但是一旦發現存在category,不論你有多少,每個都要能夠和過濾規則中的任何一個category相同。我們可以通過addCategory方法為Intent添加category。
1. 如果intent對象不包含任何category,并且該intent不是用來啟動Activity的,那么該intent對象總是能通過所有任意的intent-filter的category測試;?
2. 如果intent對象包含category(至少一個),那么只有當intent-filter中聲明的category全部包含intent對象中的所有category的時候才通過category測試。?
3. 如果允許Activity被隱式的Intent啟動,那么我們必須在該Activity的intent-filter中聲明值為android.intent.category.DEFAULT的category。
特別說明:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
這二者共同出現,標明該Activity是一個入口Activity,并且會出現在系統應用列表中,二者缺一不可。
(1)path、pathPrefix、pathPattern 之間的區別
path 用來匹配完整的路徑,如:http://example.com/blog/abc.html,這里將 path 設置為 /blog/abc.html 才能夠進行匹配;?
pathPrefix 用來匹配路徑的開頭部分,拿上來的 Uri 來說,這里將 pathPrefix 設置為 /blog 就能進行匹配了;?
pathPattern 用表達式來匹配整個路徑,這里需要說下匹配符號與轉義。?
匹配符號:?
“” 用來匹配0次或更多,如:“a” 可以匹配“a”、“aa”、“aaa”…?
“.” 用來匹配任意字符,如:“.” 可以匹配“a”、“b”,“c”…?
因此 “.*” 就是用來匹配任意字符0次或更多,如:“.*html” 可以匹配 “abchtml”、“chtml”,“html”,“sdf.html”…?
轉義:因為當讀取 Xml 的時候,“/” 是被當作轉義字符的(當它被用作 pathPattern 轉義之前),因此這里需要兩次轉義,讀取 Xml 是一次,在 pathPattern 中使用又是一次。如:“” 這個字符就應該寫成 “//”,“/” 這個字符就應該寫成 “////”。
(2)查詢是否有Activity可以匹配我們指定Intent的組件
采用PackageManager的resolveActivity或者Intent的resolveActivity方法會獲得最適合Intent的一個Activity?
調用PackageManager的queryIntentActivities會返回所有成功匹配Intent的Activity
(3)android.intent.action.MAIN 與android.intent.category.LAUNCHER的區別
區別一:?
android.intent.action.MAIN決定一個應用程序最先啟動那個組件?
android.intent.category.LAUNCHER決定應用程序是否顯示在程序列表里(說白了就是是否在桌面上顯示一個圖標)?
這兩個屬性組合情況:?
第一種情況:有MAIN,無LAUNCHER,程序列表中無圖標?
原因:android.intent.category.LAUNCHER決定應用程序是否顯示在程序列表里?
第二種情況:無MAIN,有LAUNCHER,程序列表中無圖標?
原因:android.intent.action.MAIN決定應用程序最先啟動的Activity,如果沒有Main,則不知啟動哪個Activity,故也不會有圖標出現?
所以這兩個屬性一般成對出現。?
如果一個應用中有兩個組件intent-filter都添加了android.intent.action.MAIN和?
android.intent.category.LAUNCHER這兩個屬性, 則這個應用將會顯示兩個圖標, 寫在前面的組件先運行。?
區別二:
android.intent.category.LAUNCHER:android.intent.category.LAUNCHER決定應用程序是否顯示在程序列表里,就是android開機后的主程序列表。?
android.intent.category.HOME:按住“HOME”鍵,該程序顯示在HOME列表里。
每一個通過 startActivity() 方法發出的隱式 Intent 都至少有一個 category,就是 “android.intent.category.DEFAULT”,所以只要是想接收一個隱式 Intent 的 Activity 都應該包括 “android.intent.category.DEFAULT” category,不然將導致 Intent 匹配失敗.?
首先查看Intent的過濾器(intent-filter),按照以下優先關系查找:action->data->category