Android知識點回顧之Intent/IntentFilter

星泉毅發表於2017-10-30

定義:Intent是一個訊息傳送物件,可以用它來訪問另外一個元件

可以用於以下三個場景:

  • startActivity
startActivity()複製程式碼
 startActivityForResult()複製程式碼
  • startService
    Android 5.0以上可以使用JobScheduler啟動
    所有版本可以使用:
    startService();
    bindService();複製程式碼
  • sendBroadcast
    三種呼叫方法
    sendBroadCast()複製程式碼
    sendOrderedBroadCast()複製程式碼
    sendStickyBroadcast()複製程式碼

Intent 型別

  • 顯示型別
    直接指定呼叫元件的名字,一般用於自己的APP內部
  • 隱式型別
    沒有指定元件的名字,而是宣告一般的action行為,用此方法可以允許呼叫另外一個APP的元件

啟動Service最好使用顯示Intent,否則會存在安全風險,因為無法確定service什麼時候被呼叫,而service又是使用者感知不到的元件

Android 5.0開始,如果bindService()隱式呼叫啟動service,會丟擲異常

Intent的建立

一個Intent物件包含了Android系統應該啟動哪個元件的資訊

一個Intent可以包含以下資訊:

  • componentName
    指定需要啟動元件的名字
    此項是可選的,用來區分顯示Intent和隱式Intent。
    賦值的方法有以下幾種

    • Intent的構造方法
    • setComponent()
    • setClass()
    • setClassName
  • action
    指定Intent要完成的動作,為字串常量
    action可自定義,也可使用系統的,一般自定義的話最好加上自己APP的包名字首
    一些系統標準的Action常量
    ACTION_VIEW,startActivity(),喚起系統相簿
    ACTION_SEND,startActivity(),把一些資料傳送給另外的一些APP,如email,社交軟體等
    ACTION_CALL,startActivity(),喚起打電話介面
    不定期補充...
    除了Intent定義了Action常量,其他的類也有定義,比如Settings,可以用來跳轉到系統指定的設定介面
    action賦值的方法有一下幾種:

    • Intent的構造方法
    • setAction()
  • data
    根據給定的Uri尋找匹配的目標元件,如果不給定MIME型別,會進行推導,給定MIME型別,則強制使用此型別。
    不同的Action有對應的data資料指定。
    其中data的值設定為setData(),MIME的設定為setType()
    常用值如下所示:
    tel://:      號碼資料格式,後跟電話號碼。 
    mailto://:    郵件資料格式,後跟郵件收件人地址。
    smsto://:    短息資料格式,後跟簡訊接收號碼。
    content://:   內容資料格式,後跟需要讀取的內容。 
    file://:      檔案資料格式,後跟檔案路徑。
    geo:// latitude, longitude:經緯資料格式,在地圖上顯示經緯度所指定的位置。
    Uri指定資料指向那種資料格式,MIME指定資料具體的型別。比如Image和Audio有同樣的Uri,但是MIME的型別是不同的。

    setData()和setType()會互相覆蓋,所以當需要同時設定Uri和MIME時,為了避免這種情況,可以呼叫setDataAndType()

  • category
    執行Action的附加資訊。可以放置很多category,但是大多數intent是不需要category的
    常用值如下:
    CATEGORY_DEFAULT:Android系統中預設的執行方式,按照普通Activity的執行方式執行。 
    CATEGORY_HOME:設定該元件為Home Activity。
    CATEGORY_PREFERENCE:設定該元件為Preference。 
    CATEGORY_LAUNCHER:設定該元件為在當前應用程式啟動器中優先順序最高的Activity,通常為入口ACTION_MAIN配合使用。 
    CATEGORY_BROWSABLE:設定該元件可以使用瀏覽器啟動。 
    CATEGORY_GADGET:設定該元件可以內嵌到另外的Activity中。
    通過addCategory()指定
  • extras
    用來存放需要傳遞的資料,以鍵值對的形式進行儲存和訪問
    通過putExtras()方法設定
  • flags
    設定Activity的啟動模式,例如:
    FLAG_ACTIVITY_CLEAR_TOP
    FLAG_ACTIVITY_NEW_TASK
    FLAG_ACTIVITY_NO_HISTORY
    FLAG_ACTIVITY_SINGLE_TOP
    具體的筆記放到Activity上
    通過setFlags()方法設定

顯示Intent的例子

Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);複製程式碼

隱式Intent的例子

其中resolveActivity()用來判斷Intent是否能夠被解析,防止APP崩潰

Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");
//Intent chooser = Intent.createChooser(sendIntent, title);//強制使用選擇器
//if (sendIntent.resolveActivity(getPackageManager()) != null) {
//    startActivity(chooser);
//}
// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(sendIntent);
}複製程式碼

接收一個隱式Intent

需要在manifest檔案中配置<intent-filter>,<intent-filter>包含如下三個子項:
<action>
<data>
<category>

<activity android:name="ShareActivity">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>複製程式碼

MIME型別 application/vnd.google.panorama360+jpg為指定的全景照片資料型別

<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>複製程式碼

使用PendingIntent

PendingIntent是對Intent的包裝,使外部的APP能夠像在內部APP那樣對所包含的Intent進行使用
主要有如下的使用場景:

  • Notification
  • App Widget
  • AlarmManager

取得例項的方法如下:
PendingIntent.getActivity(),啟動Activity
PendingIntent.getService(),啟動Service
PendingIntent.getBroadcast(),啟動BroadcastReceiver

狀態列通知:

  int icon = android.R.drawable.my_icon;
  long when = System.currentTimeMillis();//通知發生的時間為系統當前時間
  Notification notification = new Notification(icon, null, when);//新建一個通知,第一個引數為圖示,第二個引數為短暫提示標題,第三個為通知時間
  notification.defaults = Notification.DEFAULT_SOUND;//發出預設聲音
  notification.flags |= Notification.FLAG_AUTO_CANCEL;//點選通知後自動清除通知
  Intent openintent = new Intent(this, OtherActivity.class);
  PendingIntent contentIntent = PendingIntent.getActivity(this, 0, openintent, 0);//當點選訊息時就會向系統傳送openintent意圖
  notification.setLatestEventInfo(this, "標題", "內容", contentIntent);//setLatestEventInfo表示設定點選該通知的事件
    int notifyCode = 0;
//獲取通知管理器
  NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
  mNotificationManager.notify(notifyCode , notification);//第一個引數為自定義的通知唯一標識複製程式碼

action匹配規則

如果intent-filter設定了action的過濾規則,比如下面的XML程式碼,

<intent-filter>
    <action android:name="android.intent.action.EDIT" />
    <action android:name="android.intent.action.VIEW" />
    ...
</intent-filter>複製程式碼

那麼:

  • intent不設定action,則直接不通過
  • intent設定了多個Action,則只要有一個匹配上,則可以通過
    //不通過
    Intent intent = new Intent();複製程式碼
    //通過
    intent.setAction("android.intent.action.EDIT");複製程式碼
    //通過
    intent.setAction("android.intent.action.EDIT");
    intent.setAction("android.intent.action.test");複製程式碼

    Category匹配規則

    如果intent-filter設定了category的過濾規則,如下面的XML程式碼,
    <intent-filter>
      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
      ...
    </intent-filter>複製程式碼
    那麼:
  • Intent不設定category,則都通過,不論intent-filter設定多少category
  • intent設定了category,intent上的category能夠匹配上intent-filter設定的category,才能夠通過

比如:

//通過
Intent intent = new Intent();複製程式碼
//通過
intent.addCategory("android.intent.category.DEFAULT"),可以匹配上複製程式碼
//通過
intent.addCategory("android.intent.category.BROWSABLE")複製程式碼
//不通過
intent.addCategory("android.intent.category.DEFAULT")
intent.addCategory("android.intent.category.test")複製程式碼

Data匹配規則

intent-filter中的結構由Uri和MIME Type組成
Uri包括如下幾部分:

  • scheme:整個Uri的模式,如http,ftp,content等
    如果scheme未指定,則忽略host
    如果只指定scheme,則所有和intent-filter相同的scheme都匹配

  • host:Uri的域名,如www.google.com
    如果host未指定,則忽略port

  • port:Uri的埠

  • path:包含的路徑資訊,如:folder/subfolder/etc
    如果scheme,host都未指定,則忽略path

整體組成:://:/,如 content://com.example.project:200/folder/subfolder/etc

MIME Type:表示image/ipeg,video/*等媒體型別

匹配規則如下:
intent中的data必須和intent-filter完全匹配才能通過。有如下幾種情況:
a.intent沒有設定Uri和MIME Type,同時intent-filter也沒有設定
b.intent只包含Uri,但未包含MIME Type,同時能夠匹配上intent-filter設定的Uri,並且intent-filter為設定MIME Type
c.intent只設定MIME Type,同時intent-filter也設定了相同的MIME Type並且未指定Uri
d.intent設定了Uri和MIME Type,intent-filter只設定了MIME Type,則intent的Uri的scheme必須為content:或是file:。即,intent-filter如果沒有設定Uri,只設定了MIME Type,則Uri為content:或file:的模式。也就是說,過濾的規則希望元件能夠從檔案(file)或內容提供者(content provider)獲取到本地資料。

規則d例子,從相簿獲取圖片:

<intent-filter>
    <data android:mimeType="image/*" />
    ...
</intent-filter>複製程式碼

則intent的匹配規則為:

 Intent intent = new Intent();
 intent.setDataAndType(Uri.parse("file://test"), "image/*");
 startActivity(intent);複製程式碼

如果intent-filter設定了多個data,則intent中的data只要和其中一個匹配就可以通過

為了防止APP崩潰,所有的intent匹配規則進行呼叫前都要進行判斷處理:
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}

本知識點完~

相關文章