部落格原文: [11月的好奇心] PendingIntent 是啥?為什麼很多場景不能直接用 Intent 非要用 PendingIntent?
出於對 PendingIntent 的好奇心,翻閱了很多資料。最後發現還是官方文件描述地到位。前三段話讀下去已經解決了我的大部分疑惑。整篇讀完有種豁然開朗的快感。 等翻譯完,開始有點不解自己最初竟然會有為什麼不直接使用 Intent 代替 PendingIntent? 這樣的問題。
總之,對於對 PendingIntent 尚有疑問的同學來說,這文件是真不錯。
/**
* A description of an Intent and target action to perform with it. Instances
* of this class are created with {@link #getActivity}, {@link #getActivities},
* {@link #getBroadcast}, and {@link #getService}; the returned object can be
* handed to other applications so that they can perform the action you
* described on your behalf at a later time.
複製程式碼
這裡說PendingIntent
是Intent
行為的描述類。類似於我們用一個檔案控制程式碼,來說明它所對應的檔案。
它由這四個方法建立:getActivity
getActivities
getBroadcast
getService
。
它可以被其它應用呼叫以執行你預先宣告的行為。
這裡可以這麼對比理解: FileDescriptor 用來描述檔案 Socket 用來描述網路 PendingIntent 用來描述行為
如果把類名改為
IntentDescriptor
或許會更好理解。
*
* <p>By giving a PendingIntent to another application,
* you are granting it the right to perform the operation you have specified
* as if the other application was yourself (with the same permissions and
* identity). As such, you should be careful about how you build the PendingIntent:
* almost always, for example, the base Intent you supply should have the component
* name explicitly set to one of your own components, to ensure it is ultimately
* sent there and nowhere else.
*
複製程式碼
把 PendingIntent
交給其它應用,你就授予了它們進行其所定義的行為的權利。
所以你建立 PendingIntent
一定要格外小心:記住在你定義的 Intent
中加上限制用的包名、類名,以確保它只被你希望的物件使用。
這段是在說,我不僅告訴你你該做啥,連許可權也給你了。
這擱在軍營中,就是一道虎符啊。所以一定要宣告好把虎符具體交到誰的手中,不然會很危險。
* <p>A PendingIntent itself is simply a reference to a token maintained by
* the system describing the original data used to retrieve it. This means
* that, even if its owning application's process is killed, the
* PendingIntent itself will remain usable from other processes that
* have been given it. If the creating application later re-retrieves the
* same kind of PendingIntent (same operation, same Intent action, data,
* categories, and components, and same flags), it will receive a PendingIntent
* representing the same token if that is still valid, and can thus call
* {@link #cancel} to remove it.
*
複製程式碼
我們現在知道 PendingIntent
其實就是個 IntentDescriptor
行為描述符,但是它存到哪裡了呢? maintained by the system,和檔案一樣,被系統維護。所以無論建立它的程式是否存在,它都和檔案一樣,被系統儲存在了某個地方。只要你擁有“讀取許可權”,就能隨時獲取它。
如果建立 PendingIntent
的程式後來又要獲取相同型別的 PendingIntent
(相同的操作、相同的 Intent
action、data、categories、component、flags),那麼它會獲取到和之前相同的 token, 前提是它仍然有效。此時你可以取消它,如果你希望的話。
* <p>Because of this behavior, it is important to know when two Intents
* are considered to be the same for purposes of retrieving a PendingIntent.
* A common mistake people make is to create multiple PendingIntent objects
* with Intents that only vary in their "extra" contents, expecting to get
* a different PendingIntent each time. This does <em>not</em> happen. The
* parts of the Intent that are used for matching are the same ones defined
* by {@link Intent#filterEquals(Intent) Intent.filterEquals}. If you use two
* Intent objects that are equivalent as per
* {@link Intent#filterEquals(Intent) Intent.filterEquals}, then you will get
* the same PendingIntent for both of them.
*
複製程式碼
這段說,上面說過使用兩個相同的 Intent
會獲取到同一個 PendingIntent
,那麼什麼樣的 Intent
被認為是相同的呢?
很多人常犯的錯誤是,認為只改變 Intent#extra
內容就可以獲取到不同的 PendingIntent
。
而事實並非如此。
Intent
是否相同通過 Intent.filterEquals
判斷。見下圖。只有這個方法返回 false
, 才能得到不同的 PendingIntent
。
* <p>There are two typical ways to deal with this.
*
* <p>If you truly need multiple distinct PendingIntent objects active at
* the same time (such as to use as two notifications that are both shown
* at the same time), then you will need to ensure there is something that
* is different about them to associate them with different PendingIntents.
* This may be any of the Intent attributes considered by
* {@link Intent#filterEquals(Intent) Intent.filterEquals}, or different
* request code integers supplied to {@link #getActivity}, {@link #getActivities},
* {@link #getBroadcast}, or {@link #getService}.
*
* <p>If you only need one PendingIntent active at a time for any of the
* Intents you will use, then you can alternatively use the flags
* {@link #FLAG_CANCEL_CURRENT} or {@link #FLAG_UPDATE_CURRENT} to either
* cancel or modify whatever current PendingIntent is associated with the
* Intent you are supplying.
*/
複製程式碼
這段就比較囉嗦了,簡單兩句話說明
在某些場景有兩種方法來避免獲取到相同 PendingIntent
:
- 改變
Intent#filterEquals
中作為判斷一句的某個欄位 - 獲取
PendingIntent
時使用不同的requestCode
如果你有多個不同的 Intent
,但你只需要一個活動的 PendingIntent
。那麼你需要通過和它繫結的 Intent
來更新或移除它。