最近在寫一個鬧鐘程序的時候使用到了 PendingIntent, 而且是兩個地方用到,一個是 AlarmManager 定時的時候, 另一個是在點擊通知進入應用的時候。其實我早就想深入研究一下 PendingIntent 了,因爲我很好奇一下幾個問題:
- 已經有了 Intent, 爲什麼還有 PendingIntent?
- PendingIntent 也就幾個場景用到過,還有其他場景嗎?
- 它的內部實現。
Intent 和 PendingIntent 的區別
Intent
Intent 是意圖的意思。Android 中的 Intent 正是取自這個意思,它是一個消息對象,通過它,Android 系統的四大組件能夠方便的通信,並且保證解耦。
Intent 可以說明某種意圖,攜帶一種行爲和相應的數據,發送到目標組件。
IntentFilter 與 Intent 配套使用,它聲明瞭一個組件接受某個 Intent。
PendingIntent
PendingIntent 是對 Intent 的封裝,關鍵的不同在於:
A組件 創建了一個 PendingIntent 的對象然後傳給 B組件,B 在執行這個 PendingIntent 的 send 時候,它裏面的 Intent 會被髮送出去,而接受到這個 Intent 的 C 組件會認爲是 A 發的。
B 以 A 的權限和身份發送了這個 Intent。
比如,我們的 Activity 如果設置了 exported = false,其他應用如果使用 Intent 就訪問不到這個 Activity,但是使用 PendingIntent 是可以的。
綜上所述,PendingIntent 有兩個特點:
- 將某個動作的觸發時機交給其他應用
- 讓那個應用代表自己去執行那個動作(權限都給他了)
API
獲取 PendingIntent
- getActivity
- getActivities
- getBroadcast
- getService
- getForegroundService
爲什麼沒有 getContentProvider?
我猜測,ContentProvider 作爲一個數據源,太重要了,相當於是把數據直接暴露出去了
它們的參數都相同,都是四個:Context, requestCode, Intent, flags。Context 不必多說,要想讓其他組件代替自己辦事,當然要將自己的上下文傳給它。action, requestCode 和 Intent 共同來標誌一個行爲的唯一性,什麼意思呢?
簡單的說,我們通過相同的方法(action), 相同的 requestCode 和相同的 Intent 獲取到的 PendingIntent, 雖然可能不是同一個對象,但是,卻是代表同一個東西,之所以這樣看 flags 參數就知道了。
FLAG_ONE_SHOT: 只執行一次, 在調用了 send 以後自動調用 cancel,不能在調用 send 了。
FLAG_NO_CREATE: 不創建新的,如果我們之前設置過,這次就能獲取到,否則,返回 null。
FLAG_CANCEL_CURRENT: 如果之前設置過,就取消掉, 重新創建個新的
FLAG_UPDATE_CURRENT: 如果之前設置過,就更新它。更新什麼呢,Intent 的 Extras
FLAG_IMMUTABLE: 設置 Intent 在 send 的時候不能更改
發送 PendingIntent
send 是觸發 PendingIntent 包含的行爲,它有很多重載形式,我們通常的開發用不到他,除非我們做桌面程序開發或者 Android Framework 開發。
這裏我們只是大體說明一下,可以傳給它一個 Intent 來對它原來的 Intent 做修改,但是如果目標設置了 FLAG_IMMUTABLE 則給參數忽略。可以設置 callback 當發送完成獲得回調,並且可以通過設置handler決定回調發生的線程。
取消 PendingIntent
只有設置 PendingIntent 的原來的應用可以取消它,發送方只能發送,當一個 PendingIntent 被取消後,發送則不會成功。
PendingIntent 的使用場景
已知的使用場景是:
- 通知,在點擊通知時執行調起本應用的操作,當然也可以執行其他操作
- 鬧鐘,定時執行某個操作
- 桌面小部件,點擊小部件時執行某個操作
通知,鬧鐘,桌面小部件,都是運行在其他應用中的,但是給我們的感知就像是我們自己的應用的一部分。
內部實現
大體的原理是: A應用希望讓B應用幫忙觸發一個行爲,這是跨應用的通信,需要 Android 系統作爲中間人,這裏的中間人就是 ActivityManager。 A應用創建建 PendingIntent,在創建 PendingIntent 的過程中,向 ActivityManager 註冊了這個 PendingIntent,所以,即使A應用死了,當它再次甦醒時,只要提供相同的參數,還是可以獲取到之前那個 PendingIntent 的。當 A 將 PendingIntent 調用系統 API 比如 AlarmManager.set(),實際是將權限給了B應用,這時候, B應用可以根據參數信息,來從 ActivityManager 獲取到 A 設置的 PendingIntent。
匹配 PendingIntent 相同時,需要匹配 Intent 相同,Intent 如何匹配相同的?