Intent表明了Android應用的啓動「意圖」,Android應用將會根據Intent來啓動指定組件,至於到底啓動哪一個組件,則取決於Intent的各個屬性。下面就來介紹一下Intent的各類發展,以及Android如何根據不一樣屬性來啓動相應的組件。Intent是由Component、Action、Data、Category、Extra及Flag六部分組成的,接下來將分別對其進行詳細介紹。html
(1)Component namejava
組件名稱實際上就是一個ComponentName對象,用於標識惟一的應用程序組件,即指明瞭指望的Intent組件,這種對象的名稱是由目標組件的類名與目標組件的包名組合而成的。須要注意的是Component name是一個可選項,若是被設置,那麼Intent對象就顯式指定了要轉向的組件,若是沒有被設置,則Intent對象須要根據其餘信息進行篩選查找。android
組件名稱經過 setComponent(),setClass(),setClassName()設置,經過getComponent()獲取。git
intent.setClassName("com.example.testb", "com.example.testb.MainActivity");
ComponentName com=new ComponentName("com.example.testb","com.example.testb.MainActivity"); intent.setComponent(com);
intent.setClass(this, MainActivity.class);
ComponentName com = intent.getComponent(); String pkgName = com.getPackageName(); String className = com.getClassName();
(2)Actionweb
Action實際上就是一個描述了Intent所觸發動做名稱的字符串,在Intent類中,已經定義好多字符串常量來表示不一樣的Action,固然,開發人員也能夠自定義Action,其定義規則一樣很是簡單。動做名必須是獨一無二的字符串,因此,一個好的習慣是使用基於Java包的命名方式的命名系統。數據庫
系統定義的Activity Action常量:數組
ACTION_CALL,撥出Data裏封裝的電話號碼,直接打電話出去。瀏覽器
ACTION_DIAL,跳到撥號界面,未打出去。服務器
ACTION_ANSWER,接聽來電。網絡
ACTION_DELETE,從容器中刪除給定的數據。
ACTION_PICK,從數據中選擇一個項目 (item),將被選中的項目返回。
ACTION_DEFAULT,和 ACTION_VIEW 相同,是在數據上執行的標準動做。
ACTION_ALL_APPS,列舉全部可用的應用。
ACTION_GET_CONTENT,讓用戶選擇數據並返回。
ACTION_EDIT,爲制定的數據顯示可編輯界面。
ACTION_BUG_REPORT,顯示 activity 報告錯誤。
ACTION_SEND,由用戶指定直接發送方式進行數據發送操做。
ACTION_SENDTO,系統根據不一樣的Data類型,經過已註冊的對應Application進行數據發送操做。
ACTION_VIEW,向用戶顯示數據。
ACTION_PICK_ACTIVITY,選擇一個 activity,返回被選擇的 activity 的類(名)。
ACTION_RUN,運行數據(指定的應用),不管它(應用)是什麼。
ACTION_INSERT,在容器中插入一個空項 (item)。
ACTION_WEB_SEARCH,執行 web 搜索。
ACTION_SYNC,執行數據同步。
ACTION_MAIN,聲明程序的入口,該Action並不會接收任何數據,同時結束後也不會返回任何數據。
ACTION_CHOOSER, 選擇器
Action很大程度上決定了Intent的另外部分的結構, 就像一個方法名決定了它接受的參數和返回值同樣. 所以, 最好給Action一個最能反映其做用的名字.一個Intent對象中的Action是使用getAction()和setAction()來讀寫的。
//打電話 Intent intent = new Intent(); //記得加權限<uses-permission android:name="android.permission.CALL_PHONE"/> //Intent.ACTION_DIAL 這裏是跳到撥號界面 //Intent.ACTION_CALL 直接打電話出去 intent.setAction(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:0-123-456-789")); startActivity(intent);
// 選擇一張圖片並剪切圖片 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); intent.putExtra("crop", "true"); // 開啓剪切 intent.putExtra("aspectX", 1); // 剪切的寬高比爲1:2 intent.putExtra("aspectY", 2); intent.putExtra("outputX", 20); // 保存圖片的寬和高 intent.putExtra("outputY", 40); intent.putExtra("output", Uri.fromFile(new File("/mnt/sdcard/temp"))); // 保存路徑 intent.putExtra("outputFormat", "JPEG");// 返回格式 startActivityForResult(intent, 0);
//打開相冊 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); // 若是要限制上傳到服務器的圖片類型時能夠直接寫如:image/jpeg 、 image/png等的類型 intent.setType("image/*"); startActivityForResult(intent, 0);
其它:
MediaStore.ACTION_IMAGE_CAPTURE,打開拍照程序
// 打開拍照程序 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(intent, 0); // 取出照片數據 Bundle extras = intent.getExtras(); Bitmap bitmap = (Bitmap) extras.get("data");
com.android.camera.action.CROP,裁剪
Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, "image/*");//uri 圖片地址 // 設置裁剪 intent.putExtra("crop", "true"); // aspectX aspectY 是寬高的比例 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); // outputX outputY 是裁剪圖片寬高 intent.putExtra("outputX", 320); intent.putExtra("outputY", 320); intent.putExtra("return-data", true); startActivityForResult(intent, CROP_REQUEST_CODE);
android.provider.Settings.ACTION_WIRELESS_SETTINGS ,進入無線網絡設置界面
Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS); startActivityForResult(intent, 0);
Media.RECORD_SOUND_ACTION,打開錄音機
Intent intent = new Intent(Media.RECORD_SOUND_ACTION); startActivity(intent);
(3)Data
Data主要是對Intent消息中數據的封裝,主要描述Intent的動做所操做到的數據的URI及類型。不一樣類型的Action會有不一樣的Data封裝,例如打電話的Intent會封裝tel://格式的電話URI,而ACTION_VIEW的Intent中Data則會封裝http:格式的URI。正確的Data封裝對Intent匹配請求一樣很是重要。
當匹配intent和可以處理intent所帶的數據的組件時,知道數據類型(MIME類型)是很重要的。好比,一個展現圖像的組件不該該被叫去播放一個音頻。
不少狀況下,從URI能夠看出數據類型,好比content: URIs,表示數據是在設備上,可是是由content provider控制。
Intent.setData(Uri); //一個Uri,Scheme包含在其中 Intent.setType(String); //指定MimeType,好比'image/jpeg', 'audio/mpeg'等 Intent.setDataAndType(Uri, String); //上面二個方法的簡便調用方式,一塊兒搞進去
geo:latitude,longitude(?q=street+address),打開地圖應用程序並顯示指定的經緯度(地址)
http://,打開瀏覽器程序並顯示指定的URL
tel:,打開電話應用程序並撥打指定的電話號碼
voicemail:,打開電話應用程序並撥下指定語音郵箱的電話號碼
mailto:,郵件數據格式,後跟郵件收件人地址。
smsto:,短息數據格式,後跟短信接收號碼。
content://,內容數據格式,後跟須要讀取的內容。
file://,文件數據格式,後跟文件路徑。
market://search?q=搜索條件,在Google Market裏搜索應用。
market://details?id=app_id|packagename,打開Google Market直接進入該程序的詳細頁面。
數據類型也能夠顯式指定,好比setData()方法指定數據爲URI,setType() 指定爲MIME type,setDataAndType() 指定它既爲URI又爲MIME type。讀取的時候URI用getData(),MIME type用getType()。
//顯示網頁 Uri uri = Uri.parse("http://www.oschina.net"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent);
//顯示地圖(北緯39.9,東經116.3) Uri uri = Uri.parse("geo:39.9,116.3"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent);
//發送短信 記得加上<uses-permission android:name="android.permission.SEND_SMS"/> Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("smsto:0800000123")); intent.putExtra("sms_body", "The SMS text"); startActivity(intent); //傳送彩信(至關於發送帶附件的短信) Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra("sms_body", "some text"); intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://media/external/images/media/23")); intent.setType("image/png"); startActivity(intent);
//播放多媒體 Uri uri = Uri.parse("file:///sdcard/song.mp3"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); intent.setType("audio/mp3"); startActivity(intent); //或者 Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "song.mp3"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent);
//打開Google Market尋找某個應用 Uri uri = Uri.parse("market://search?q=開源中國"); Intent it = new Intent(Intent.ACTION_VIEW, uri); startActivity(it); //顯示某個應用的相關信息 Uri uri = Uri.parse("market://details?id=com.oschina.test"); Intent it = new Intent(Intent.ACTION_VIEW, uri); startActivity(it);
//卸載應用程序 Uri uri = Uri.fromParts("package", strPackageName, null); Intent intent = new Intent(Intent.ACTION_DELETE, uri); startActivity(intent); //安裝應用程序 Intent intent = new Intent(Intent.ACTION_VIEW); Uri uri = Uri.parse("file:///sdcard/solitaire.apk"); intent.setDataAndType(uri,"application/vnd.android.package-archive"); startActivity(intent); //或者 Uri uri = Uri.fromParts("package", strPackageName, null); Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED, uri);
//打開聯繫人列表 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("vnd.android.cursor.item/phone"); startActivityForResult(intent, 0); //或者 content://contacts/people(/+聯繫人ID) Uri uri = Uri.parse("content://contacts/people"); Intent intent = new Intent(Intent.ACTION_PICK, uri); startActivityForResult(intent, 0);
//打開另外一程序 Intent intent = new Intent(Intent.ACTION_MAIN); ComponentName cn = new ComponentName("com.example.testb","com.example.testb.MainActivity"); intent.setComponent(cn); startActivityForResult(intent, 0);
常見的MIME類型:
超文本標記語言文本 .html,.html text/html
普通文本 .txt text/plain
RTF文本 .rtf application/rtf
GIF圖形 .gif image/gif
JPEG圖形 .ipeg,.jpg image/jpeg
au聲音文件 .au audio/basic
MIDI音樂文件 .mid,.midi audio/midi,audio/x-midi
RealAudio音樂文件 .ra, .ram audio/x-pn-realaudio
MPEG文件 .mpg,.mpeg video/mpeg
AVI文件 .avi video/x-msvideo
GZIP文件 .gz application/x-gzip
TAR文件 .tar application/x-tar
(4)Category
Category是對目標組件類別信息的描述。一樣做爲一個字符串對象,一個Intent中能夠包含多個Category。與Category相關的方法有三個,addCategory添加一個Category,removeCategory刪除一個Category,而getCategories獲得一個Category。Android系統一樣定義了一組靜態字符常量來表示Intent的不一樣類別,下面列出一些常見的Category常量。
CATEGORY_ALTERNATIVE:你將在這章的後面所看到的,一個Intent Filter的用途是使用動做來幫忙填入上下文菜單。ALTERNATIVE種類指定,在某種數據類型的項目上能夠替代默認執行的動做。例如,一個聯繫人的默認動做時瀏覽它,替代的多是去編輯或刪除它。
CATEGORY_APP_BROWSER:和ACTION_MAIN一塊兒使用,用來啓動瀏覽器應用程序。
CATEGORY_APP_CALCULATOR:和ACTION_MAIN一塊兒使用,用來啓動計算器應用程序。
CATEGORY_APP_CALENDAR:和ACTION_MAIN一塊兒使用,用來啓動日曆應用程序。
CATEGORY_APP_CONTACTS:和ACTION_MAIN一塊兒使用,用來啓動聯繫人應用程序。
CATEGORY_APP_EMAIL:和ACTION_MAIN一塊兒使用,用來啓動郵件應用程序。
CATEGORY_APP_GALLERY:和ACTION_MAIN一塊兒使用,用來啓動圖庫應用程序。
CATEGORY_APP_MAPS:和ACTION_MAIN一塊兒使用,用來啓動地圖應用程序。
CATEGORY_APP_MARKET:這個activity容許用戶瀏覽和下載新的應用程序。
CATEGORY_APP_MESSAGING:和ACTION_MAIN一塊兒使用,用來啓動短信應用程序。
CATEGORY_APP_MUSIC:和ACTION_MAIN一塊兒使用,用來啓動音樂應用程序
CATEGORY_SELECTED_ALTERNATIVE:設置這個activity是否能夠被認爲是用戶當前選擇的數據的一個可選擇的action,與ALTERNATIVE相似,但ALTERNATIVE老是使用下面所述的Intent解析來指向單一的動做。SELECTED_ALTERNATIVE在須要一個可能性列表時使用。
CATEGORY_BROWSABLE:設置該組件可使用瀏覽器啓動。
CATEGORY_DEFAULT:Android系統中默認的執行方式,按照普通Activity的執行方式執行。
CATEGORY_GADGET:設置該組件能夠內嵌到另外的Activity中。
CATEGORY_HOME:HOME Activity是設備啓動(登錄屏幕)時顯示的第一個Activity。經過指定Intent Filter爲HOME種類而不指定動做的話,你正在將其設爲本地home畫面的替代。
CATEGORY_LAUNCHER:使用這個種類來讓一個Activity做爲應用程序的啓動項。
CATEGORY_MONKEY:這個activity可能被monkey或者其餘的自動測試工具執行。
CATEGORY_OPENABLE :用來指示一個GET_CONTENT意圖只但願ContentResolver.openInputStream可以打開URI。
CATEGORY_TAB:代表目標Activity是TabActivity的一個標籤下的Activity。
CATEGORY_PREFERNCE:設置該組件爲Preference。
CATEGORY_EMBED:可以在上級(父)activity 中運行。
CATEGORY_DEVELOPMENT_PREFERENCE:說明 activity 是一個設置面板 (development preference panel)。
CATEGORY_SAMPLE_CODE :做爲一個簡單的代碼示例使用(通常狀況下不使用)。
CATEGORY_DESK_DOCK:指定手機被插入桌面底座(硬件)時運行該Activity。
CATEGORY_CAR_DOCK:指定手機被插入汽車底座(硬件)時運行該Activity。
CATEGORY_CAR_MODE:設置該Activity可在車載環境下使用。
CATEGORY_INFO:用於提供包信息。
CATEGORY_TEST:該Activity是一個測試。
CATEGORY_UNIT_TEST:聯合測試使用。
一個Intent最多隻能包含一個Action屬性,可是一個Intent中能夠包含多個Category屬性。
//喚起MAIN和LAUNCHER的應用 Intent intent = new Intent(Intent.ACTION_MAIN,null); intent.addCategory(Intent.CATEGORY_LAUNCHER); startActivity(intent);
你會發現喚起的應用裏沒有本身的應用,由於經過category篩選屬於implicit intent的調用方式,不屬於指定軟件包名及類名的explicit intent的精確調用方式,對於implicit intent調用須要進行聲明<category android:name="android.intent.category.DEFAULT"/>,以下:
<activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity>
(5)Extra
Extra中封裝了一些額外的附加信息,這些信息是以鍵值對的形式存在的。Intent能夠經過putExtras()與getExtras()方法來存儲和獲取Extra。在Android系統的Intent類中,一樣對一些經常使用的Extra鍵值進行了定義,以下所示。
EXTRA_BCC:存放郵件密送人地址的字符串數組。
EXTRA_CC:存放郵件抄送人地址的字符串數組。
EXTRA_EMAIL:存放郵件地址的字符串數組。
EXTRA_SUBJECT:存放郵件主題字符串。
EXTRA_TEXT:存放郵件內容。
EXTRA_KEY_EVENT:以KeyEvent對象方式存放觸發Intent的按鍵。
EXTRA_PHONE_NUMBER:存放調用ACTION_CALL時的電話號碼。
EXTRA_SHORTCUT_ICON:使用ACTION_CREATE_SHORTCUT在HomeActivity建立快捷方式時,對快捷方式的描述信息。(其中ICON和ICON_RESOURCE描述的是快捷方式的圖標,類型分別爲Bitmap和ShortcutIconResource。INTENT描述的是快捷方式相對應的Intent對象。NAME描述的是快捷方式的名字。)
EXTRA_SHORTCUT_ICON_RESOURCE EXTRA_SHORTCUT_INTENT EXTRA_SHORTCUT_NAME EXTRA_SUBJECT 描述信息主題的鍵
EXTRA_TITLE 使用ACTION_CHOOSER動做時,描述對話框標題的鍵
EXTRA_UID 使用ACTION_UID_REMOVED動做時,描述刪除的用戶id的鍵
// 給someone@domain.com發郵件發送內容爲「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);
// 給多人發郵件 Intent intent=new Intent(Intent.ACTION_SEND); String[] tos = {"1@abc.com", "2@abc.com"}; // 收件人 String[] ccs = {"3@abc.com", "4@abc.com"}; // 抄送 String[] bccs = {"5@abc.com", "6@abc.com"}; // 密送 intent.putExtra(Intent.EXTRA_EMAIL, tos); intent.putExtra(Intent.EXTRA_CC, ccs); intent.putExtra(Intent.EXTRA_BCC, bccs); intent.putExtra(Intent.EXTRA_SUBJECT, "Subject"); intent.putExtra(Intent.EXTRA_TEXT, "Hello"); intent.setType("message/rfc822"); startActivity(intent);
//調用系統編輯添加聯繫人 Intent intent = newIntent(Intent.ACTION_INSERT_OR_EDIT); intent.setType(People.CONTENT_ITEM_TYPE); intent.putExtra(Contacts.Intents.Insert.NAME, "My Name"); intent.putExtra(Contacts.Intents.Insert.PHONE, "+1234567890"); intent.putExtra(Contacts.Intents.Insert.PHONE_TYPE,Contacts.PhonesColumns.TYPE_MOBILE); intent.putExtra(Contacts.Intents.Insert.EMAIL, "com@com.com"); intent.putExtra(Contacts.Intents.Insert.EMAIL_TYPE,Contacts.ContactMethodsColumns.TYPE_WORK); startActivity(intent);
//自定義一個chooser,不使用系統的chooser 該chooser能夠有本身的標題(Title) Intent intent = new Intent(); intent.setAction(Intent.ACTION_CHOOSER); intent.putExtra(Intent.EXTRA_TITLE, "my chooser"); intent.putExtra(Intent.EXTRA_INTENT,new Intent(Intent.ACTION_GET_CONTENT).setType("*/*").addCategory(Intent.CATEGORY_OPENABLE)); startActivityForResult(intent, 0);
//啓動搜索,在如下示例代碼中,"ANDROID"爲要搜索的字符串,當執行這段代碼後, 會在系統的Chooser中顯示能夠用於搜索的程序列表 Intent intent = new Intent(); intent.setAction(Intent.ACTION_SEARCH); //啓動搜索 intent.putExtra(SearchManager.QUERY, "ANDROID"); startActivity(intent);
//啓動WEB搜索,在如下示例代碼中,"ANDROID"爲要搜索的字符串,當執行這段代碼後, 會在系統的Chooser中顯示能夠用於搜索的程序列表,通常狀況下系統中安裝的瀏覽器都會顯示出來 Intent intent = new Intent(); intent.setAction(Intent.ACTION_WEB_SEARCH); //啓動搜索 intent.putExtra(SearchManager.QUERY, "ANDROID"); startActivity(intent);
(6)Flag
首先簡單介紹下Task和Activity的關係:
Task就像一個容器,而Activity就至關與填充這個容器的東西,第一個東西(Activity)則會處於最下面,最後添加的東西(Activity)則會在最上面。從Task中取出東西(Activity)是從最頂端取出,也就是說最早取出的是最後添加的東西(Activity),以此類推,最後取出的是第一次添加的Activity,而Activity在Task中的順序是能夠控制的,在Activity跳轉時用到Intent Flag能夠設置新建activity的建立方式。
(1) 前提: Activity A和Activity B在同一個應用中.
操做: Activity A啓動開僻Task堆棧(堆棧狀態: A), 在Activity A中啓動Activity B(堆棧狀態: AB), 按下BACK返回鍵(堆棧狀態: A).
(2) 前提: Activity A和Activity B在同一個應用中, 應用名稱爲"TaskOne應用".
操做: 在Launcher中單擊"TaskOne應用"圖標, Activity A啓動開僻Task堆棧, 命名爲TaskA(TaskA堆棧狀態: A),在Activity A中啓動Activity B(TaskA堆棧狀態: AB), 長按Home鍵, 返回Launcher, 啓動其它應用(如:電子書),開僻一個新Task堆棧, 命名: TaskB, 長按Home健, 返回Launcher, 單擊"TaskOne應用"圖標, 此時TaskA堆棧返回前臺,Activity B爲棧頂應用, 供用戶使用.
(3) 前提: Activity A在名稱爲"TaskOne應用"的應用中, Activity C在名稱爲"TaskTwo應用"的應用中.
操做: 在Launcher中單擊"TaskOne應用"圖標, Activity A啓動開僻Task堆棧, 命名爲TaskA(TaskA堆棧狀態: A),在Activity A中啓動Activity C(TaskA堆棧狀態: AC),長按Home鍵, 返回Launcher, 啓動"TaskTwo應用"即Activity C,開僻新的Task堆棧, 命名爲TaskB, 按BACK鍵返回Launcher, 單擊"TaskOne應用"圖標, 此時TaskA堆棧返回前臺,Activity C爲棧頂應用, 供用戶使用.
①FLAG_ACTIVITY_NEW_TASK: 設置此狀態,記住如下原則,首先會查找是否存在和被啓動的Activity具備相同的親和性的任務棧(即taskAffinity,注意同一個應用程序中的activity的親和性同樣,因此下面的a狀況會在同一個棧中,前面這句話有點拗口,請多讀幾遍),若是有,剛直接把這個棧總體移動到前臺,並保持棧中的狀態不變,即棧中的activity順序不變,若是沒有,則新建一個棧來存放被啓動的activity。
a). 前提: Activity A和Activity B在同一個應用中。
操做: Activity A啓動開僻Task堆棧(堆棧狀態: A), 在Activity A中啓動Activity B, 啓動Activity B的Intent的Flag設爲FLAG_ACTIVITY_NEW_TASK, Activity B被壓入Activity A所在堆棧(堆棧狀態: AB)。
緣由: 默認狀況下同一個應用中的全部Activity擁有相同的關係(taskAffinity).
b). 前提: Activity A在名稱爲"TaskOne應用"的應用中, Activity C和Activity D在名稱爲"TaskTwo應用"的應用中。
操做1: 在Launcher中單擊"TaskOne應用"圖標, Activity A啓動開僻Task堆棧, 命名爲TaskA(TaskA堆棧狀態: A),在Activity A中啓動Activity C, 啓動Activity C的Intent的Flag設爲FLAG_ACTIVITY_NEW_TASK,Android系統會爲Activity C開僻一個新的Task, 命名爲TaskB(TaskB堆棧狀態: C), 長按Home鍵, 選擇TaskA,Activity A回到前臺, 再次啓動Activity C(兩種狀況1.從桌面啓動;2.從Activity A啓動,兩種狀況同樣), 這時TaskB回到前臺, Activity C顯示, 供用戶使用, 即:包含FLAG_ACTIVITY_NEW_TASK的Intent啓動Activity的Task正在運行, 則不會爲該Activity建立新的Task,而是將原有的Task返回到前臺顯示。
操做2: 在Launcher中單擊"TaskOne應用"圖標, Activity A啓動開僻Task堆棧, 命名爲TaskA(TaskA堆棧狀態: A),在Activity A中啓動Activity C,啓動Activity C的Intent的Flag設爲FLAG_ACTIVITY_NEW_TASK,Android系統會爲Activity C開僻一個新的Task, 命名爲TaskB(TaskB堆棧狀態: C), 在Activity C中啓動Activity D(TaskB的狀態: CD) 長按Home鍵, 選擇TaskA, Activity A回到前臺, 再次啓動Activity C(從桌面或者ActivityA啓動,也是同樣的),這時TaskB回到前臺, Activity D顯示,供用戶使用.說明了在此種狀況下設置FLAG_ACTIVITY_NEW_TASK後,會先查找是否是有Activity C存在的棧,根據親和性(taskAffinity),若是有,剛直接把這個棧總體移動到前臺,並保持棧中的狀態不變,即棧中的順序不變。
②FLAG_ACTIVITY_CLEAR_TOP:
前提: Activity A, Activity B, Activity C和Activity D在同一個應用中.
操做: Activity A啓動開僻Task堆棧(堆棧狀態: A), 在Activity A中啓動Activity B(堆棧狀態: AB), 在Activity B中啓動Activity C(堆棧狀態: ABC), 在Activity C中啓動Activity D(堆棧狀態: ABCD), 在Activity D中啓動Activity B,啓動Activity B的Intent的Flag設置爲FLAG_ACTIVITY_CLEAR_TOP, (堆棧狀態: AB)。
③FLAG_ACTIVITY_BROUGHT_TO_FRONT:
前提: Activity A在名稱爲"TaskOne應用"的應用中, Activity C和Activity D在名稱爲"TaskTwo應用"的應用中.
操做: 在Launcher中單擊"TaskOne應用"圖標, Activity A啓動開僻Task堆棧, 命名爲TaskA(TaskA堆棧狀態: A),在Activity A中啓動Activity C,啓動Activity C的Intent的Flag設爲FLAG_ACTIVITY_NEW_TASK,Android系統會爲Activity C開僻一個新的Task, 命名爲TaskB(TaskB堆棧狀態: C), 在Activity C中啓動Activity D(TaskB的堆棧狀態: CD), 長按Home鍵, 選擇TaskA, Activity A回到前臺, 在Activity A中再次啓動Activity C,在啓動Activity C的Intent中設置Flag爲FLAG_ACTIVITY_BROUGHT_TO_FRONT, TaskB回到前臺,Activity C顯示, (TaskB的堆棧狀態: C)。
④FLAG_ACTIVITY_MULTIPLE_TASK:
與FLAG_ACTIVITY_NEW_TASK結合使用, 首先在Intent中設置FLAG_ACTIVITY_NEW_TASK, 打開Activity,則啓動一個新Task, 接着在Intent中設置FLAG_ACTIVITY_MULTIPLE_TASK, 再次打開同一個Activity,則還會新啓動一個Task。
⑤FLAG_ACTIVITY_SINGLE_TOP:
當前Task堆棧中存在ABCD四個Activity, A是棧頂Activity, D爲棧底Activity, 存在打開A的Intent中設置了FLAG_ACTIVITY_SINGLE_TOP標誌, 則會使用棧頂A, 而不會重新New A.
⑥FLAG_ACTIVITY_RESET_TASK_IF_NEEDED:
通常與FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET結合使用,若是設置該屬性,這個activity將在一個新的task中啓動或者或者被帶到一個已經存在的task的頂部,這時這個activity將會做爲這個task的首個頁面加載。將會致使與這個應用具備相同親和力的task處於一個合適的狀態(移動activity到這個task或者從中移出),或者簡單的重置這個task到它的初始狀態FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET:在當前的Task堆棧中設置一個還原點,當帶有FLAG_ACTIVITY_RESET_TASK_IF_NEEDED的Intent請求啓動這個堆棧時(典型的例子是用戶從桌面再次啓動這個應用),還原點之上包括這個應用將會被清除。應用場景:在email程序中預覽圖片時,會啓動圖片觀覽的actvity,當用戶離開email處理其餘事情,而後下次再次從home進入email時,咱們呈現給用戶的應該是上次email的會話,而不是圖片觀覽,這樣纔不會給用戶形成困惑。
例: 存在Activity A, Activity B, Activity C, Activity A啓動開僻Task堆棧, 命名爲TaskA(TaskA堆棧狀態: A),在Activity A中啓動Activity B(TaskA堆棧狀態: AB), 接着Activity B啓動Activity C(TaskA堆棧狀態: ABC),啓動Activity C的Intent中設置FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET標題, 這樣TaskA中有一個還原點,當有包含FLAG_ACTIVITY_RESET_TASK_IF_NEEDED的Intent請求TaskA堆棧時(好比請求Activity A),系統就會將還原點以上的Activity清除, TaskA堆棧中只剩下了AB。
例子:
Intent intent = new Intent(); intent.setClassName("com.example.testb", "com.example.testb.MainActivity"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent);
IntentFilter實際上至關於Intent的過濾器,一個應用程序開發完成後,須要告訴Android系統本身可以處理哪些隱形的Intent請求,這就須要聲明IntentFilter。IntentFilter的使用方法實際上很是簡單,僅聲明該應用程序接收什麼樣的Intent請求便可。
IntentFilter過濾Intent時,通常是經過Action、Data及Category三方面進行監測的。接下來分別對這三方面進行介紹。
(1)檢查Action
一個Intent只能設置一種Action,可是一個IntentFilter卻能夠設置多個Action過濾。當IntentFilter設置了多個Action時,只需一個知足便可完成Action驗證。當IntentFilter中沒有說明任何一個Action時,那麼任何的Action都不會與之匹配。而若是Intent中沒有包含任何Action,那麼只要IntentFilter中含有Action時,便會匹配成功。
String MY_ACTION = "com.view.my_action";//自定義 Intent intent = new Intent(MY_ACTION); startActivity(intent);
<activity android:name=".TestActivity" android:launchMode="singleTop"> <intent-filter> <action android:name="com.view.my_action"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity>
(2)檢查Data
數據的監測主要包含兩部分,即數據的URI及數據類型,而數據URI又被分紅三部分進行匹配(scheme、authority、path),只有這些所有匹配時,Data的驗證纔會成功。
若是你的組件能處理多個的話,你能夠包含多個條件。你可使用下面屬性的任意組合來指定組件支持的數據:
android:host 指定一個有效的主機名(例如,com.google)。
android:mimetype
容許你設定組件能處理的數據類型。例如,<type android:value=」vnd.android.cursor.dir/*」/>能匹配任何Android遊標。
android:path 有效地URI路徑值(例如,/transport/boats/)。
android:port 特定主機上的有效端口。
android:scheme 須要一個特殊的圖示(例如,content或http)。
例如,下面的URI: content://com.example.project:200/folder/subfolder/etc
scheme
是content,host是"com.example.project",port是200,path是"folder/subfolder/etc"。host和port一塊兒構成URI的憑據(authority),若是host沒有指定,port也被忽略。
這四個屬性都是可選的,但它們之間並不都是徹底獨立的。要讓authority有意義,scheme必須也要指定。要讓path有意義,scheme和authority也都必需要指定。
當比較intent對象和過濾器的URI時,僅僅比較過濾器中出現的URI屬性。例如,若是一個過濾器僅指定了scheme,全部有此scheme的URIs都匹配過濾器;若是一個過濾器指定了scheme和authority,但沒有指定path,全部匹配scheme和authority的URIs都經過檢測,而無論它們的path;若是四個屬性都指定了,要都匹配才能算是匹配。然而,過濾器中的path能夠包含通配符來要求匹配path中的一部分。
<data>元素的type屬性指定數據的MIME類型。Intent對象和過濾器均可以用"*"通配符匹配子類型字段,例如"text/*","audio/*"表示任何子類型。
數據檢測既要檢測URI,也要檢測數據類型。規則以下:
① 一個Intent對象既不包含URI,也不包含數據類型:僅當過濾器也不指定任何URIs和數據類型時,纔不能經過檢測;不然都能經過。
② 一個Intent對象包含URI,但不包含數據類型:僅當過濾器也不指定數據類型,同時它們的URI匹配,才能經過檢測。例如,mailto:和tel:都不指定實際數據。
③ 一個Intent對象包含數據類型,但不包含URI:僅當過濾也只包含數據類型且與Intent相同,才經過檢測。
④ 一個Intent對象既包含URI,也包含數據類型(或數據類型可以從URI推斷出):數據類型部分,只有與過濾器中之一匹配纔算經過;URI部分,它的URI要出如今過濾器中,或者它有content:或file: URI,又或者過濾器沒有指定URI。換句話說,若是它的過濾器僅列出了數據類型,組件假定支持content:和file: 。
若是一個Intent可以經過不止一個活動或服務的過濾器,用戶可能會被問那個組件被激活。若是沒有目標找到,會產生一個異常。
Intent intent = new Intent("com.view.my_action"); intent.setData(Uri.parse("oschina://com.example.project")); startActivity(intent);
<activity android:name=".MainActivity" android:label="@string/title_notes_list"> <intent-filter> <action android:name="com.view.my_action"/> <category android:name="android.intent.category.DEFAULT"/> <data android:scheme="oschina" android:host="com.example.project"/> </intent-filter> </activity>
(3)檢查Category
IntentFilter一樣能夠設置多個Category,當Intent中的Category與IntentFilter中的一個Category徹底匹配時,便會經過Category的檢查,而其餘的Category並不受影響。可是當IntentFilter沒有設置Category時,只能與沒有設置Category的Intent相匹配。
其它問題:
Intent在尋找目標組件時有兩種方法:
第一種,顯式調用(Explicit Intent),經過Component name直接指定;
第二種,隱式調用(implicit Intent),沒有明確指定目標組件的名稱,那麼就要經過必定的條件過濾篩選。
Implicit Intent 到底發給哪一個activity?
這須要進行三個匹配,一個是action,一個是category,一個是data。根據三個的匹配結果,找到應該啓動的Activity。
Data匹配時的規則一共有四條:
a.若是Intent沒有指定Data相關的字段,只能匹配上沒有指定Data的IntentFilter。也就是說若是一個Intent沒有指定任何的Data(Uri和Type),它只能匹配到沒有指定任何Data(Scheme和Type)的IntentFilter。
b.若是一個Intent只指定了Uri可是沒有Type(而且Type也不可以從Uri中分析出)只能匹配到僅指定了相應Scheme且沒有指定Type的IntentFilter。實際的例子有若是一個Intent是想要發郵件,或是打電話,它們的Intent是相似這樣的:"mailto:someone@sb.com"和"tel:1234567"。換句話說,這些Uri自己就是數據,而再也不是一個指向數據的地址。好比:Phone中的Dialer就有以下的IntentFilter:
<intent-filter> <action android:name="android.intent.action.CALL" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="tel" /> </intent-filter>
再如,要處理SD狀態變化的IntentFilter:
<intent-filter> <action android:name="android.intent.action.MEDIA_MOUNTED"/> <action android:name="android.intent.action.MEDIA_UNMOUNTED"/> <action android:name="android.intent.action.MEDIA_SHARED"/> <action android:name="android.intent.action.MEDIA_REMOVED"/> <action android:name="android.intent.action.MEDIA_EJECT"/> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="file" /> </intent-filter>
再如,要處理Package狀態變化的IntentFilter:
<intent-filter> <action android:name="android.intent.action.PACKAGE_ADDED"/> <action android:name="android.intent.action.PACKAGE_REMOVED"/> <action android:name="android.intent.action.PACKAGE_DATA_CLEARED"/> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="package" /> <intent-filter>
可是注意,對於想對數據進行操做的Intent,最好不要只指定Uri,而不指定類型。由於若是這樣作一般會匹配到一大堆
c. 若是一個Intent只指定了Type,可是沒有指定Uri,它只能匹配到只指定了相應Type且沒有指定Scheme的IntentFitler
d. 若是一個Intent即有Uri又有Type,那麼它會匹配上:1).Uri和Type都匹配的IntentFilter;2).首先Type要匹配,另外若是Intent的Uri是content:或file:,且IntentFilter沒有指定Scheme的IntentFilter。由於對於Android來說content和file這二種Scheme是系統最多見也是用的最多的,因此就當成缺省值來對待。
另外須要注意,Type,由於是MimeType,因此是容許使用通配符的,好比'image/*',能匹配上全部以'image'爲開頭的類型,也說是說能匹配上全部的圖像。
根據Data匹配的例子
假如系統中有四個Activity,A的IntentFilter是這樣子的:
<activity ...> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="content" android:mimeType="image/*" /> </intent-filter> </activity>
這代表A能夠發送一切圖片類型,而且內容必須是由ContentProvider提供的,也就是Uri必須是以"content://"開頭的
而另一個Activity B是這樣子聲明的:
<activity ...> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="file" android:mimeType="image/*" /> </intent-filter> </activity>
這代表B能夠發送一切圖片,但內容必須是單獨的一個文件,也就是Uri必須是由"file://"開頭的
還有一個C是這樣子聲明的:
<activity ...> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
這代表C只能接收那些沒有指定任何Uri和Type的Action是SEND的Intent。
而D是這樣子聲明的:
<activity ...> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="image/*" /> </intent-filter> </activity>
這代表D能夠發送一切圖片,不管是數據庫內的(content),仍是單獨的文件(file)。
若是一個Intent是這樣寫的:
Intent share = new Intent(Intent.ACTION_SEND); startActivity(share);
那麼它只能匹配C,由於C沒有指定數據和類型,Action是SEND,根據規則a,它只能匹配Activity A。但若是給Intent加上額外的條件
share.setDataAndType(uri,"image/jpeg");
那麼若是uri是數據庫內容,它會匹配到A,若是它是一個文件,會匹配到B。但不管是content仍是file都會匹配到D,由於它能處理以任何形式存儲的圖片。但始終不會匹配到C,由於C沒有聲明Data字段,因此不會匹配上。
因此,一般想把組件做爲系統公用接口時都是這樣子來寫:
<activity ...> <intent-filter> <!-- implement public actions such as View, Edit, Pick or Send --> <action android:name="android.intent.action.SEND" /> <!-- never forget default category, otherwise your activity never receives intents --> <category android:name="android.intent.category.DEFAULT" /> <!-- specify mimeType to constrain data type, receive data from both content provider and file --> <data android:mimeType="image/*" /> <!-- specify scheme to constrain data source, if necessary --> <data android:shceme="http" /> </intent-filter> </activity>
Intent和IntentFilter對於組件Activity來說注意事項比較多,可是對於Service和BroadcastReceiver來講就沒有那麼多的注意事項了,由於對於Service和BroadcastReceiver一般都不用設置Category和Data。但也有例外,好比前面所講到的SD相關廣播和應用程序安裝相關廣播。
另外要注意,若是使用Context.startActivity()或Context.startActivityForResult(),Context.bindService()和Context.startService(),若是系統沒有爲Intent匹配到目標Activity和Service那麼會有RuntimeException(ActivityNotFoundException)拋出;若是有多個目標同時匹配,會以列表的方式來讓用戶選擇使用哪一個。