在前 4 篇文章中,咱們介紹了 Android 四大組件的基礎知識,四大組件是構成咱們 App 的基礎,也是 Android 系統設計的最佳體現。各個組件之間徹底是解耦的,若是想訪問其餘組件或者啓動其餘組件可使用 Intent
來操做。在四種組件類型中,有三種(Activity、Service 和 Broadcast)都可以經過異步消息 Intent 進行啓動。Intent 會在運行時對各個組件進行互相綁定。因此咱們能夠把 Intent
看成是各個組件之間的信使(不管該組件是本身 App 的仍是其餘 App)。javascript
每一個組件都有不一樣的啓動方法:css
如要啓動 Activity,能夠用 startActivity() 或 startActivityForResult() 傳遞 Intent(須要 Activity 返回結果時)html
如要啓動 Service,能夠經過 startService()傳遞 Intent 來啓動服務,也可經過 bindService() 傳遞 Intent 來綁定到該服務java
如要發送 Broadcast,能夠經過 sendBroadcast()、sendOrderedBroadcast() 或 sendStickyBroadcast() 等方法傳遞 Intent 來發起廣播android
與 Activity、Service 和 Broadcast,ContentProvider 並不是由 Intent 啓動。相反,它們會在成爲 ContentResolver 的請求目標時啓動。想要訪問 ContentProvider 的內容,能夠經過 ContentResolver 調用 query()、insert()、update()、delete()等方法。瀏覽器
Intent
是一個消息傳遞對象,用來向其餘組件請求操做,不管該組件是當前應用仍是其餘應用。具體來講包含三大用處: 啓動一個 Activity、啓動或者綁定 Service、傳遞廣播。安全
Intent 分爲兩種類型:app
假設有一個 Activity A 須要啓動一個 Activity B。若是經過顯示 Intent 啓動時,系統會當即啓動該組件。使用隱式 Intent 時,Android 系統經過將 Intent 的內容與在設備上其餘應用的清單文件中聲明的 Intent 過濾器進行比較,從而找到要啓動的相應組件。若是 Intent 與 Intent 過濾器匹配,則系統將啓動該組件,並向其傳遞 Intent 對象。若是多個 Intent 過濾器兼容,則系統會顯示一個對話框,支持用戶選取要使用的應用。大體流程以下圖:異步
特別注意:ide
爲了確保應用的安全性,啓動 Service 時,必須使用顯式 Intent,且不要爲服務聲明 Intent 過濾器。使用隱式 Intent 啓動服務存在安全隱患,由於沒法肯定哪些服務將響應 Intent,且用戶沒法看到哪些服務已啓動。從 Android 5.0(API 級別 21)開始,若是使用隱式 Intent 調用 bindService(),系統會拋出異常。
一個 Intent 對象會攜帶一些信息,Android 系統會根據這些信息來肯定要啓動哪一個組件。具體來講會攜帶如下七種信息: ComponentName、Action、Category、Data 、Type、Extra、Flags
。咱們能夠把這七種信息稱之爲 Intent 的七大屬性
。下面咱們具體的來分析每個屬性的含義和使用方法:
ComponentName:要啓動的組件名稱
指定了ComponentName 屬性的 Intent 已經明確了它將要啓動哪一個具體組件,所以這種 Intent 被稱爲顯式 Intent,沒有指定 ComponentName 屬性的 Intent 被稱爲隱式 Intent。隱式 Intent 沒有明確要啓動哪一個組件,應用會根據 Intent 指定的其餘信息去啓動符合條件的組件。代碼示例://聲明一個顯示意圖
Intent intent = new Intent();
ComponentName componentName = new ComponentName(MainActivity.this,SecondActivity.class);
intent.setComponent(componentName);
startActivity(intent);
複製代碼
除了經過 setComponent
爲 intent 指定要啓動的組件名稱外,還可使用 Intent 的構造函數:
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
複製代碼
使用 Intent 構造函數設置組件名稱的方式,代碼更加的簡潔。
Action:操做
當咱們要聲明一個隱式意圖的時候,就要用到 Action,它指定了被啓動的組件要完成的具體的操做,好比我想啓動一個能夠瀏覽照片的 Activity,能夠設置 Action 爲 ACTION_VIEW
,或者啓動一個能夠發送郵件的 Activity,能夠設置 Action 爲 ACTION_SEND
。Action 一般是和 Category 結合起來使用。Action 自己是一個字符串,除了系統定義好的,咱們也能夠本身定義,下面經過代碼來演示經過定義一個隱式 Intent 來啓動 Activity:step1:在啓動方定義一個隱式 Intent
Intent intent = new Intent();
//自定義了一個action:com.cyy.jump
intent.setAction("com.cyy.jump");
//必須指定一個category
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(intent);
複製代碼
step2:爲被啓動方配置 <intent-filter>
。具體作法是在 AndroidManifest.xml
中,爲被啓動的 Activity 設置上 :
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.cyy.jump" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
複製代碼
在 <intent-filter>
中,咱們設置了兩個屬性:<action>
和 <category>
。的值必須與啓動方的 setAction 的值同樣:com.cyy.jump。
下面咱們運行一下項目,看看效果:
跳轉成功。
假如咱們爲多個 Activity 配置了一樣的 會有什麼效果呢?
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.cyy.jump" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".ThirdActivity">
<intent-filter>
<action android:name="com.cyy.jump" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
複製代碼
咱們運行一下,看看效果:
能夠看到,在同一個應用內,若是有多個 Activity 設置了一樣的 ,當在調用 startActivity() 後,系統會彈出一個選擇框,讓用戶本身選擇須要跳轉的 Activity。
這是啓動同一個應用內的頁面,假設其餘應用的某個 Activity 也設置了一樣的 呢?咱們能夠來試驗一下,新建一個項目,名叫 IntentDemo2,在 IntentDemo2 中新建一個頁面叫作 HomeActivity:
<activity android:name=".HomeActivity">
<intent-filter>
<action android:name="com.cyy.jump" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
複製代碼
再次運行項目,看看效果:
能夠看到,在對話框中顯示出了 IntentDemo2 的選項,點擊便可跳轉 IntentDemo2 的 HomeActivity 頁面。
以上是自定義的 Action,下面咱們來演示一下使用系統定義的 Action,以 ACTION_SEARCH
爲例:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEARCH);
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(intent);
複製代碼
運行項目,查看效果:
此時,系統也會彈出全部知足 ACTION_SEARCH
的應用列表提供給用戶選擇。
系統定義的一些 Action:
Action | 含義 |
---|---|
ACTION_MAIN | Android 的程序入口 |
ACTION_VIEW | 顯示指定數據 |
ACTION_EDIT | 編輯指定數據 |
ACTION_DIAL | 顯示撥號面板 |
ACTION_CALL | 直接呼叫 Data 中所帶的號碼 |
ACTION_ANSWER | 接聽來電 |
ACTION_SEND | 向其餘人發送數據(例如:彩信/email) |
以上只是簡單列舉了一部分,更多的能夠參考官方文檔:Intent
Category:類別
Category 屬性爲 Action 增長額外的附加類別信息。經常使用的如 CATEGORY_DEFAULT
,表示 Android 系統中默認的執行方式,按照普通的 Activity 的執行方式執行。在好比 CATEGORY_LAUNCHER
,設置該組件爲當前應用程序啓動器中優先級最高的,一般與程序入口動做 ACTION_MAIN
配合使用:<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
複製代碼
Data & Type:數據&類型
Data 屬性一般是爲 Action 屬性提供要操做的數據,例如撥打指定電話、發送短信時指定電話號碼和短信內容等數據。Data 屬性的值是一個 Uri
對象,格式以下:schema://host:port/path
複製代碼
schema
協議host
主機port
端口path
路徑如:http://www.baidu.com:8080/a.jpg
系統內置的幾個 Data 屬性常量
協議 | 含義 |
---|---|
tel: | 號碼數據格式,後跟電話號碼 |
mailto: | 郵件數據格式,後跟郵件收件人地址 |
smsto: | 短信數據格式,後跟短信接收號碼 |
file:/// | 文件數據格式,後跟文件路徑。注意必須是三根斜槓 /// |
content:// | 內容數據格式,後跟須要讀取的內容。ContentProvider 特有的格式 |
舉幾個例子給你們展現一下:
例1:打開一個網頁
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
複製代碼
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("tel:18755555555"));
startActivity(intent);
複製代碼
Type
屬性用於指定 Data 所制定的 Uri 對應的
MIME
類型,一般應用於調用系統 App,好比實現查看文件(文本、圖片、音頻或者視頻等),經過指定文件的 MIME 類型,可讓系統知道用什麼程序打開該文件。
設置 Data 時,調用 setData()
,設置 Type 時,調用 setType
,注意,這兩個方法不能同時設置,會被覆蓋掉。若是想要同時設置 Data 和 Type,請調用 setDataAndType
。咱們能夠查看一下源碼:
/** * Set the data this intent is operating on. This method automatically * clears any type that was previously set by {@link #setType} or * {@link #setTypeAndNormalize}. * * <p><em>Note: scheme matching in the Android framework is * case-sensitive, unlike the formal RFC. As a result, * you should always write your Uri with a lower case scheme, * or use {@link Uri#normalizeScheme} or * {@link #setDataAndNormalize} * to ensure that the scheme is converted to lower case.</em> * * @param data The Uri of the data this intent is now targeting. * * @return Returns the same Intent object, for chaining multiple calls * into a single statement. * * @see #getData * @see #setDataAndNormalize * @see android.net.Uri#normalizeScheme() */
public @NonNull Intent setData(@Nullable Uri data) {
mData = data;
mType = null;
return this;
}
......
/** * Set an explicit MIME data type. * * <p>This is used to create intents that only specify a type and not data, * for example to indicate the type of data to return. * * <p>This method automatically clears any data that was * previously set (for example by {@link #setData}). * * <p><em>Note: MIME type matching in the Android framework is * case-sensitive, unlike formal RFC MIME types. As a result, * you should always write your MIME types with lower case letters, * or use {@link #normalizeMimeType} or {@link #setTypeAndNormalize} * to ensure that it is converted to lower case.</em> * * @param type The MIME type of the data being handled by this intent. * * @return Returns the same Intent object, for chaining multiple calls * into a single statement. * * @see #getType * @see #setTypeAndNormalize * @see #setDataAndType * @see #normalizeMimeType */
public @NonNull Intent setType(@Nullable String type) {
mData = null;
mType = type;
return this;
}
......
/** * (Usually optional) Set the data for the intent along with an explicit * MIME data type. This method should very rarely be used -- it allows you * to override the MIME type that would ordinarily be inferred from the * data with your own type given here. * * <p><em>Note: MIME type and Uri scheme matching in the * Android framework is case-sensitive, unlike the formal RFC definitions. * As a result, you should always write these elements with lower case letters, * or use {@link #normalizeMimeType} or {@link android.net.Uri#normalizeScheme} or * {@link #setDataAndTypeAndNormalize} * to ensure that they are converted to lower case.</em> * * @param data The Uri of the data this intent is now targeting. * @param type The MIME type of the data being handled by this intent. * * @return Returns the same Intent object, for chaining multiple calls * into a single statement. * * @see #setType * @see #setData * @see #normalizeMimeType * @see android.net.Uri#normalizeScheme * @see #setDataAndTypeAndNormalize */
public @NonNull Intent setDataAndType(@Nullable Uri data, @Nullable String type) {
mData = data;
mType = type;
return this;
}
複製代碼
下面咱們經過一個例子來講明一下 Data 和 Type 的用法:
查看手機裏的一張圖片,地址爲:storage/emulated/0/DCIM/IMG_201910297_162012_328.jpg
File file = new File("storage/emulated/0/DCIM/IMG_201910297_162012_328.jpg");
Intent intent = new Intent();
Uri uri = Uri.fromFile(file);
intent.setDataAndType(uri, "image/jpeg");
startActivity(intent);
複製代碼
運行查看效果:
經常使用的 MIME 類型:
文件格式 | 對應的MIME類型 |
---|---|
.bmp | image/bmp |
.gif | image/gif |
.png | image/png |
.tif .tiff | image/tiff |
.jpe .jpeg .jpg | image/jpeg |
.txt | text/plain |
.xml | text/xml |
.html | text/html |
.css | text/css |
.js | text/javascript |
.mht .mhtml | message/rfc822 |
.doc | application/msword |
.docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document |
.rtf | application/rtf |
.xls | application/vnd.ms-excel application/x-excel |
.xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
.ppt | application/vnd.ms-powerpoint |
.pptx | application/vnd.openxmlformats-officedocument.presentationml.presentation |
.pps | application/vnd.ms-powerpoint |
.ppsx | application/vnd.openxmlformats-officedocument.presentationml.slideshow |
application/pdf | |
.swf | application/x-shockwave-flash |
.dll | application/x-msdownload |
.exe | application/octet-stream |
.msi | application/octet-stream |
.chm | application/octet-stream |
.cab | application/octet-stream |
.ocx | application/octet-stream |
.rar | application/octet-stream |
.tar | application/x-tar |
.tgz | application/x-compressed |
.zip | application/x-zip-compressed |
.z | application/x-compress |
.wav | audio/wav |
.wma | audio/x-ms-wma |
.wmv | video/x-ms-wmv |
.mp3 .mp2 .mpe .mpeg .mpg | audio/mpeg |
.rm | application/vnd.rn-realmedia |
.mid .midi .rmi | audio/mid |
Extra:額外
屬性用於添加一些附加信息,它的屬性值是一個 Bundle 對象,經過鍵值對的形式存儲數據。在隱式 Intent 中使用較少,主要用於顯示 Intent 傳遞數據。下面簡單演示一下://MainActivity
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("name", "我叫 Android");
startActivity(intent);
複製代碼
//SecondActivity
Intent intent = getIntent();
if (intent != null) {
String name = intent.getStringExtra("name");
textView.setText(name);
}
複製代碼
Flags:標記
一般用來動態配置 Activity 的啓動模式。大部分狀況下,咱們都不須要設置 Flags,因此,對於 Flags 你們可以理解就行。下面,介紹幾個經常使用的:
FLAG_ACTIVITY_NEW_TASK:設置這個標記位的話,是爲 Activity 指定 「singleTask」 啓動模式,它的做用和在清單文件中指定該啓動模式的效果同樣。
FLAG_ACTIVITY_SINGLE_TOP:設置這個標記位的話,是爲 Activity 指定 「singleTop」 啓動模式,它的做用和在清單文件中指定該啓動模式的效果同樣。
FLAG_ACTIVITY_CLEAR_TOP:具備此標記位的 Activity ,在它啓動時,在同一個任務棧中全部位於它上面的 Activity 都要出棧。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具備這個標記的 Activity 不會出如今歷史 Activity 的列表中。它等同於在清單文件中指定 Activity 的屬性 android:excludeFromRecents="true"
。
在上一節的內容中,咱們介紹了 Intent 的七大屬性,也給你們演示瞭如何定義一個顯示 Intent和隱式 Intent,顯示 Intent 的使用更加的簡單,所以咱們就不過多介紹了,下面再給你們分析一下隱式 Intent。
要聲明組件能夠接收哪些隱式 Intent,須要在清單文件中使用 <intent-filter>
元素爲組件聲明一個或多個
Intent 過濾器。每一個 <intent-filter>
中主要設置的屬性包括:<action>
、<data>
、<category>
。當隱式 Intent 能夠匹配上其中一個<intent-filter>
時,系統就會將該 Intent 傳遞給應用組件(顯式 Intent 始終會傳遞給其目標組件,不管目標組件聲明的 <intent-filter>
是什麼)。
應用組件應該爲自身可執行的每一個獨特做業聲明單獨的 <intent-filter>
。例如,圖像庫應用中的一個 Activity 可能會有兩個 <intent-filter>
,分別用於查看圖像和編輯圖像。當 Activity 啓動時,將檢查 Intent 並根據 Intent 中的信息決定具體的行爲(例如,是否顯示編輯圖片控件)。
特別注意:在 <intent-filter>
中,必須設置一個默認的 <category>
:<category android:name="android.intent.category.DEFAULT" />
,否者組件不會接收隱式 Intent。
假如咱們不但願其餘應用啓動咱們的組件,只但願在本應用中使用組件,那麼咱們就不要在清單中聲明 <intent-filter>
,而且將該組件的 exported
屬性設置爲 false
。
下面咱們經過一個具體的例子來講明一下 <intent-filter>
的用法:
<activity android:name="MainActivity">
<!-- This activity is the main entry, should appear in app launcher -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="ShareActivity">
<!-- This activity handles "SEND" actions with text data -->
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
<!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.SEND_MULTIPLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="application/vnd.google.panorama360+jpg"/>
<data android:mimeType="image/*"/>
<data android:mimeType="video/*"/>
</intent-filter>
</activity>
複製代碼
第一個 Activity MainActivity 是應用的主要入口:
ACTION_MAIN
操做指示這是主要入口,且不要求輸入任何 Intent 數據。CATEGORY_LAUNCHER
類別指示此 Activity 的圖標應放入系統的應用啓動器。若是 元素未使用 icon 指定圖標,則系統將使用 元素中的圖標。這兩個元素必須配對使用,Activity 纔會顯示在應用啓動器中。
第二個 Activity ShareActivity 旨在便於共享文本和媒體內容。儘管能夠經過從 MainActivity 進入此 Activity,但也能夠從發出隱式 Intent(與兩個 Intent 過濾器之一匹配)的另外一應用中直接進入 ShareActivity。
MIME 類型 application/vnd.google.panorama360+jpg 是一個指定全景照片的特殊數據類型
注意1:當沒有任何應用可以響應咱們調用 startActivity() 傳遞的隱式 Intent 時如何處理?
咱們自定義一個 Action,可是不讓任何 Activity 接收該 Intent:
Intent intent = new Intent();
intent.setAction("com.cyy.send");
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(intent);
複製代碼
運行後,應用奔潰,Error 信息以下:
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=com.cyy.send cat=[android.intent.category.DEFAULT] }
爲了不這種狀況的出現,咱們在調用 startActivity() 前,須要調用 resolveActivity()
驗證是否有 Activity 能夠接收 Intent,具體作法以下:
Intent intent = new Intent();
intent.setAction("com.cyy.send");
intent.addCategory(Intent.CATEGORY_DEFAULT);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
複製代碼
若是 resolveActivity 的結果爲非空,則表示至少有一個應用可以處理該 Intent,此時便可安全的調用 startActivity()。
注意2:當有多個應用能夠響應咱們的隱式 Activity 時,系統會彈出一個選擇框,讓用戶選擇須要打開的應用,用戶也能夠選擇記住要本身打開的應用,這樣下次就不會再彈出選擇框。那麼假如我但願每次都彈窗,不讓用戶記住呢?咱們可使用 createChooser()
建立 Intent。
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
String title = "請選擇瀏覽器";
// Create intent to show the chooser dialog
Intent chooser = Intent.createChooser(intent, title);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(chooser);
}
複製代碼
運行效果以下: