Android 面試題:說一下 PendingIntent 和 Intent 的區(qū)別

請點贊,你的點贊對我意義重大,滿足下我的虛榮心。

?? Hi,我是小彭。本文已收錄到 GitHub · Android-NoteBook 中。這里有 Android 進(jìn)階成長知識體系,有志同道合的朋友,歡迎跟我一起成長。

前言

  • 從字面意思上理解,PendingIntent 是一種延遲的 Intent,表示一種延遲執(zhí)行的意圖操作。對,但又不完全對。 一句話概括,PendingIntent 一種是支持授權(quán)其他應(yīng)用以當(dāng)前應(yīng)用的身份執(zhí)行包裝的 Intent 操作的系統(tǒng)特性。
  • 在這篇文章里,我將帶你理解 PendingIntent 的使用方法、設(shè)計理念以及核心源碼分析,相信閱讀完這篇文章后你對 PendingIntent 的理解將超過絕大部分同學(xué)。如果能把幫上忙,請務(wù)必點贊加關(guān)注,你的支持對我非常重要。

1. 認(rèn)識 PendingIntent

1.1 為什么要使用 PendingIntent?

PendingIntent 的應(yīng)用場景關(guān)鍵在于間接的 Intent 跳轉(zhuǎn)需求, 即先通過一級 Intent 跳轉(zhuǎn)到某個組件,在該組件完成任務(wù)后再間接地跳轉(zhuǎn)到二級的 Intent。PendingIntent 中的單詞 “pending” 指延遲或掛起,就是指它是延遲的或掛起的。例如,你在以下場景中就可以使用 PendingIntent:

  • 場景 1 - 系統(tǒng)通知消息的點擊操作
  • 場景 2 - 桌面微件的點擊操作
  • 場景 3 - 系統(tǒng)鬧鐘操作
  • 場景 4 - 第三方應(yīng)用回調(diào)操作

可以看到,在這些場景中,我們真正感興趣的操作是掛起的,并且該操作并不是由當(dāng)前應(yīng)用執(zhí)行,而是由某個外部應(yīng)用來 “間接” 執(zhí)行的。例如,我們在發(fā)送系統(tǒng)通知消息時,會通過 PendingIntent 構(gòu)造一個系統(tǒng)通知 Notification ,并調(diào)用 NotificationManagerCompat.notify(…) 發(fā)送通知,此時并不會直接執(zhí)行 PendingIntent。而是當(dāng)系統(tǒng)顯示通知,并且用戶點擊通知時,才會由系統(tǒng)通知這個系統(tǒng)應(yīng)用間接執(zhí)行 PendingIntent#send() ,而不是通過當(dāng)前應(yīng)用執(zhí)行。

當(dāng)然,在低版本系統(tǒng)中,你還可以使用嵌套 Intent(Intent#extra 中嵌套另一個 Intent)來實現(xiàn)以上需求。但是從 Android 12 開始,嵌套 Intent 將被嚴(yán)格禁止,原因下文會說。

1.2 PendingIntent 和 Intent 有什么區(qū)別?

從結(jié)構(gòu)上來說,PendingIntent 是 Intent 的包裝類,其內(nèi)部持有一個代表最終意圖操作的 Intent(事實上,內(nèi)部是通過 IIntentSender 間接持有)。它們的區(qū)別我認(rèn)為可以概括為 3 個維度:

  • 1、執(zhí)行進(jìn)程不同 —— PendingIntent 在其他進(jìn)程執(zhí)行: Intent 通常會在創(chuàng)建進(jìn)程中執(zhí)行,而 PendingIntent 通常不會在創(chuàng)建進(jìn)程中執(zhí)行;
  • 2、執(zhí)行時間不同 —— PendingIntent 會延遲執(zhí)行: Intent 通常會立即執(zhí)行,而 PendingIntent 通常會延遲執(zhí)行,延遲到其他進(jìn)程完成任務(wù)后再執(zhí)行,甚至延遲到創(chuàng)建進(jìn)程消亡后。例如,在 場景 1 - 系統(tǒng)通知消息的點擊操作 中,即使發(fā)送系統(tǒng)通知消息的進(jìn)程已經(jīng)消亡了,依然不妨礙二級 Intent 的跳轉(zhuǎn);
  • 3、執(zhí)行身份不同 —— PendingIntent 支持授權(quán): PendingIntent 內(nèi)部持有授權(quán)信息,支持其他應(yīng)用以當(dāng)前應(yīng)用的身份執(zhí)行,這有利于避免嵌套 Intent 存在的安全隱患。而直接使用 Intent 的話,一般只能以當(dāng)前應(yīng)用的身份執(zhí)行(為什么說一般?因為有 Activity#startActivityAsUser() 這個 API,但一般你拿不到所需的參數(shù))。

提示: 當(dāng)然了,如果你創(chuàng)建 PendingIntent 后又馬上同步地在當(dāng)前進(jìn)程消費這個 PendingIntent,那么時間維度上就沒區(qū)別了。但是這樣做其實不符合 PendingIntent 的應(yīng)用場景。

1.3 嵌套 Intent 存在的安全隱患

上文提到,在低版本系統(tǒng)中,你可以使用嵌套 Intent 實現(xiàn)類似于 PendingIntent 的需求。但這一方案從 Android 12 開始被嚴(yán)格禁止,為什么呢 —— 存在安全隱患。

舉個例子,我們將啟動 ClientCallbackActivity 的 Intent 嵌套到啟動 ApiService 的 Intent 里,實現(xiàn)一個 場景 4 - 第三方應(yīng)用回調(diào)操作 的效果:

  • 步驟 1: Client App 請求 Provider App 的一個服務(wù)(這通過一級 Intent 實現(xiàn));
  • 步驟 2: Provider App 在任務(wù)結(jié)束后回調(diào)到 Client App 的 ClientCallbackActivity(這通過嵌套的二級 Intent 實現(xiàn))。

該過程用示意圖表示如下:

乍看起來沒有問題,但其實存在 2 個隱蔽的安全隱患:

  • 隱患 1 - Client App: 由于 ClientCallbackActivity 是從另一個應(yīng)用 Provider App 啟動的,因此該 Activity 必須暴露為 exported。這意味著除了 Provider App 可以啟動該 Activity 外,同時也給了惡意應(yīng)用啟動該 Activity 的可能性。如果 ClientCallbackActivity 是一個普通的 Activity 還要說,要是 ClientCallbackActivity 是一個敏感或高風(fēng)險的行為(例如支付回調(diào)),那么這就存在很大的安全隱患了;
  • 隱患 2 - Provider App: 由于嵌套的 Intent 是在 Provider App 的上下文中啟動的,那么二級 Intent 不僅可以正常啟動 Client App 中的 ClientCallbackActivity(打開 exported 時),還可以啟動 Provider App 中任意 Activity。這意味著給了惡意應(yīng)用啟動 Provider App 中敏感或高風(fēng)險的 Activity 的可能性,即使這個敏感的 Activity 事先已經(jīng)關(guān)閉 exported。這說明 exported 機制失效了,也存在很大的安全隱患。

該攻擊過程用示意圖表示如下:

解決方法是使用 PendingIntent 代替嵌套 Intent,此時這兩個風(fēng)險都不存在。為什么呢?—— 因為 PendingIntent 將以 Client App(PendingIntent 的創(chuàng)建進(jìn)程)的身份執(zhí)行,而不是 Provider App (PendingIntent 的消費進(jìn)程)的身份執(zhí)行。

現(xiàn)在,我們再回顧下還有沒有安全隱患:

  • 隱患 1 - Client App: 由于 PendingIntent 使用 Client App 的身份執(zhí)行,那么 ClientCallbackActivity 不再需要暴露為 exported。此時,惡意應(yīng)用不存在常規(guī)啟動 ClientCallbackActivity 的可能性,風(fēng)險解除;
  • 隱患 2 - Provider App: 由于 PendingIntent 使用 Client App / Attacker App 的身份執(zhí)行,而它們是沒有權(quán)限訪問 Provider App 非 exported 的 ApiSensitiveActivity 的。此時,惡意應(yīng)用不能啟動 ApiSensitiveActivity,風(fēng)險解除。

該過程用示意圖表示如下:

提示: 擔(dān)心有的同學(xué)鉆牛角這里再補充一下:如果我的二級 Intent 就是想要回調(diào)到 Provider App 中的 ApiSensitiveActivity 那怎么辦?很簡單,說明 Client 并不關(guān)心回調(diào),那么就直接使用 Intent 即可,Provider App 內(nèi)部的回調(diào)行為交給其內(nèi)部處理。


2. PendingIntent 的使用方法

2.1 創(chuàng)建 PendingIntent

PendingIntent 支持在啟動 Activity、Service 或 BroadcastReceiver。不同類型的組件必須使用特定的靜態(tài)方法:

示例程序

// 啟動 Activity
PendingIntent.getActivity(Context context, int requestCode, Intent intent, int flags)
// 啟動 Service
PendingIntent.Service(Context context, int requestCode, Intent intent, int flags)
// 啟動 BroadcastReceiver(發(fā)送廣播)
PendingIntent.getBroadcast(Context context, int requestCode, Intent intent, int flags)

創(chuàng)建 PendingIntent 后,就可以將 PendingIntent 發(fā)送給其他應(yīng)用,例如發(fā)送到系統(tǒng)通知消息:

示例程序

// 通知構(gòu)造器
NotificationManagerCompat compat = NotificationManagerCompat.from(context);
NotificationCompat.Builder builder = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    builder = new NotificationCompat.Builder(context, CHANNEL_ID);
} else {
    builder = new NotificationCompat.Builder(context);
}
...
// 設(shè)置 PendingIntent
builder.setContentIntent(pendingIntent);
// 構(gòu)造通知
Notification notification = builder.build()
// 發(fā)送通知
compat.notify(NOTIFICATION_TAG, NOTIFICATION_ID, notification);

簡單說明下創(chuàng)建 PendingIntent 的 4 個參數(shù):

  • 1、context: 當(dāng)前應(yīng)用的上下文,PendingIntent 將從中抽取授權(quán)信息;
  • 2、requestCode: PendingIntent 的請求碼,與 Intent 的請求碼類似;
  • 3、intent: 最終的意圖操作;
  • 4、flag: 控制標(biāo)記位,我們暫且放到一邊。

創(chuàng)建 PendingIntent 時有一個容易犯錯的地方需要注意:重復(fù)調(diào)用 PendingIntent.getActivity() 等創(chuàng)建方法不一定會返回新的對象,系統(tǒng)會基于兩個要素判斷是否需要返回相同的 PendingIntent:

  • 要素 1 - requestCode: 不同的 requestCode 會被認(rèn)為不同的 PendingIntent 意圖;
  • 要素 2 - Intent: 不同的 Intent 會被認(rèn)為不同的 PendingIntent 意圖,但并不是 Intent 中所有的參數(shù)都會參與計算,而是僅包含 Intent.filterEquals() 方法考慮的參數(shù),即:action、data、type、identity、class 和 categories,但不包括 extras。

2.2 消費 PendingIntent

上面提到 PendingIntent 是 Intent 的嵌套類,那么在消費 PendingIntent 時是否可以從中取出嵌套的 Intent 再執(zhí)行 startActivity 之類的方法呢?NO!消費 PendingIntent 的方法只能使用 PendingIntent#send() 相關(guān)重載方法。例如:

PendingIntent.java

public void send() throws CanceledException {
    send(null, 0, null, null, null, null, null);
}

public void send(Context context, int code, @Nullable Intent intent) throws CanceledException {
    send(context, code, intent, null, null, null, null);
}

關(guān)于 send() 內(nèi)部的實現(xiàn)原理,我們在下一節(jié)原理分析中再說。

2.3 取消 PendingIntent

調(diào)用 PendingIntent#cancel() 方法可以取消已經(jīng)創(chuàng)建的 PendingIntent,該方法將從系統(tǒng)中移除已經(jīng)注冊的 PendingIntent(事實上,是移除 IIntentSender)。如果后續(xù)繼續(xù)消費這個已經(jīng)被取消的 PendingIntent,將拋出 CanceledException 異常。

PendingIntent.java

private final IIntentSender mTarget;

public void cancel() {
    ActivityManager.getService().cancelIntentSender(mTarget);
}

2.4 可變性與不可變性

PendingIntent 可變性是一種對外部應(yīng)用消費行為的約束機制,通過標(biāo)記位 FLAG_MUTABLEFLAG_IMMUTABLE 控制 PendingIntent 可變或不可變。例如:

示例程序

// 創(chuàng)建可變 PendingIntent
val pendingIntent = PendingIntent.getActivity(applicationContext, NOTIFICATION_REQUEST_CODE, intent, PendingIntent.FLAG_MUTABLE)

// 創(chuàng)建不可變 PendingIntent
val pendingIntent = PendingIntent.getActivity(applicationContext, NOTIFICATION_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE)

那么,可變性意味著什么呢?可變性意味著在消費 PendingIntent 時,可以針對其中包裝的 Intent 進(jìn)行修改,即使用 PendingIntent#send(Context, int, Intent) 進(jìn)行修改。需要注意的是,這里的 Intent 參數(shù)并不會完全替換 PendingIntent 中包裝的 Intent,而是將修改的信息填充到原有的 Intent 上。

源碼摘要

// send() 內(nèi)部通過 Intent#fillIn() 修改 Intent,而不是替換 Intent

// PendingIntent#send() 最終執(zhí)行到:
int changes = finalIntent.fillIn(intent, key.flags);

例如,以下為修改可變 PendingIntent 示例:

示例程序

val intentWithExtrasToFill = Intent().apply {
    putExtra(EXTRA_CUSTOMER_MESSAGE, customerMessage)
}
mutablePendingIntent.send(applicationContext, PENDING_INTENT_CODE, intentWithExtrasToFill)

// 至此,PendingIntent 內(nèi)部包裝的 Intent 將持有 EXTRA_CUSTOMER_MESSAGE 信息

另外,PendingIntent 可變性的注意事項:

  • 注意事項 1 - 修改不可變 PendingIntent: 即使是不可變的 PendingIntent 類型,創(chuàng)建 PendingIntent 的應(yīng)用總是可以修改,因為可變性只是對外部應(yīng)用消費行為的約束。例如:

修改示例

// 創(chuàng)建不可變 PendingIntent
val pendingIntent = PendingIntent.getActivity(applicationContext, NOTIFICATION_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE)

// 在當(dāng)前應(yīng)用修改不可變 PendingIntent,需要使用 PendingIntent.FLAG_UPDATE_CURRENT 標(biāo)記位
val updatedPendingIntent = PendingIntent.getActivity(applicationContext, NOTIFICATION_REQUEST_CODE, anotherIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
  • 注意事項 2 - 顯式指定可變性: FLAG_MUTABLE 可變標(biāo)記位是 Android 12 新增的,在 Android 12 之前,未使用 FLAG_IMMUTABLE 不可變標(biāo)記位的 PendingIntent 都默認(rèn)是可變的。但是,從 Android 12 開始,為了使 PendingIntent 的處理更加安全,系統(tǒng)要求 PendingIntent 必須顯式聲明一個可變性標(biāo)志。這個問題我們在 Android 系統(tǒng)適配手冊 里講到過。
  • 注意事項 3 - 可變 PendingIntent 需要使用顯式 Intent: 可變 PendingIntent 應(yīng)該將其中包裝的 Intent 設(shè)置為顯式 Intent,確保修改后的 PendingIntent 沒有安全隱患。

2.5 PendingIntent 標(biāo)記位

現(xiàn)在,我們回過頭再總結(jié)一下 PendingIntent 的 flags 標(biāo)記位:

  • FLAG_IMMUTABLE: 不可變標(biāo)記位,將約束外部應(yīng)用消費 PendingIntent 修改其中的 Intent;
  • FLAG_MUTABLE: 可變標(biāo)記位,不約束外部應(yīng)用消費 PendingIntent 修改其中的 Intent;
  • FLAG_UPDATE_CURRENT: 更新標(biāo)記位 1,如果系統(tǒng)中已經(jīng)存在相同的 PendingIntent,那么將保留原有 PendingIntent 對象,而更新其中的 Intent。即使不可變 PendingIntent,依然可以在當(dāng)前應(yīng)用更新;
  • FLAG_CANCEL_CURRENT: 更新標(biāo)記位 2,如果系統(tǒng)中已經(jīng)存在相同的 PendingIntent,那么將先取消原有的 PendingIntent,并重新創(chuàng)建新的 PendingIntent。
  • FLAG_NO_CREATE: 更新標(biāo)記位 3,如果系統(tǒng)中已經(jīng)存在相同的 PendingIntent,那么不會重新創(chuàng)建,而是直接返回 null;
  • FLAG_ONE_SHOT: 一次有效標(biāo)記位,PendingIntent 被消費后不支持重復(fù)消費,即只能使用一次。

3. PendingIntent 實現(xiàn)原理分析

3.1 創(chuàng)建 PendingIntent 的執(zhí)行過程

創(chuàng)建 PendingIntent 需要使用特定的靜態(tài)方法,內(nèi)部會通過 Binder 通信將 PendingIntent 意圖注冊到 AMS 系統(tǒng)服務(wù)進(jìn)程中,并獲得一個 Binder 對象 IIntentSender。關(guān)鍵源碼摘要如下:

PendingIntent.java

private final IIntentSender mTarget;

// 此處運行在應(yīng)用進(jìn)程
public static PendingIntent getActivity(Context context, int requestCode, Intent intent, @Flags int flags) {
    return getActivity(context, requestCode, intent, flags, null);
}

public static PendingIntent getActivity(Context context, int requestCode, @NonNull Intent intent, @Flags int flags, @Nullable Bundle options) {
    String packageName = context.getPackageName();
    String resolvedType = intent != null ? intent.resolveTypeIfNeeded(context.getContentResolver()) : null;
    intent.migrateExtraStreamToClipData(context);
    intent.prepareToLeaveProcess(context);
    // 通過 Binder 通信注冊 Intent,得到 IIntentSender
    IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature(
        ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
        context.getAttributionTag(), null, null, requestCode, new Intent[] { intent },
        resolvedType != null ? new String[] { resolvedType } : null,
        // 注意這個參數(shù),使用當(dāng)前應(yīng)用的 UserId
        flags, options, context.getUserId());
    return new PendingIntent(target);
}

ActivityManagerService.java

// 此處運行在 AMS 系統(tǒng)服務(wù)進(jìn)程
public IIntentSender getIntentSenderWithFeature(int type, String packageName, String featureId,
    IBinder token, String resultWho, int requestCode, Intent[] intents,
    String[] resolvedTypes, int flags, Bundle bOptions, int userId) {
    ...
    int callingUid = Binder.getCallingUid();
    return mPendingIntentController.getIntentSender(type, packageName, featureId,
        callingUid /*調(diào)用應(yīng)用進(jìn)程*/, userId /*原始應(yīng)用進(jìn)程*/, token, resultWho, requestCode, intents, resolvedTypes,
        flags, bOptions);
}

PendingIntentController.java

// 存儲已注冊的 pendingIntent 記錄
final HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> mIntentSenderRecords = new HashMap<>();

// 此處運行在 AMS 系統(tǒng)服務(wù)進(jìn)程
public PendingIntentRecord getIntentSender(int type, String packageName,
    @Nullable String featureId, int callingUid, int userId, IBinder token, String resultWho,
    int requestCode, Intent[] intents, String[] resolvedTypes, int flags, Bundle bOptions) {
        
    // 構(gòu)建 PendingIntent 的 Key
    PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, featureId,token, resultWho, requestCode, intents, resolvedTypes, flags, SafeActivityOptions.fromBundle(bOptions), userId);

    WeakReference<PendingIntentRecord> ref = mIntentSenderRecords.get(key);

    // 此處處理以下標(biāo)記位的邏輯
    // FLAG_NO_CREATE
    // FLAG_CANCEL_CURRENT
    // FLAG_UPDATE_CURRENT

    if(ref != null) {
        return ref;
    }
    rec = new PendingIntentRecord(this, key, callingUid);
    mIntentSenderRecords.put(key, rec.ref);
    return rec;
}

PendingIntentRecord.java

public final class PendingIntentRecord extends IIntentSender.Stub {

    final static class Key {

        // 關(guān)鍵參數(shù):創(chuàng)建進(jìn)程的 UserId
        final int userId;

        Key(int _t, String _p, ..., int _userId) {
            ...
            userId = _userId;
        }

        public boolean equals(Object otherObj) {
            ...
            // 要素 1 - requestCode 源碼體現(xiàn)
            if (requestCode != other.requestCode) {
                return false;
            }
            // 要素 2 - Intent 源碼體現(xiàn)
            if (requestIntent != other.requestIntent) {
                if (requestIntent != null) {
                    if (!requestIntent.filterEquals(other.requestIntent)) {
                        return false;
                    }
                } else if (other.requestIntent != null) {
                    return false;
                }
            }
        }
    }   
}

至此,PendingIntent 就在系統(tǒng)進(jìn)程中以 PendingIntentRecord 記錄的形式存在,相當(dāng)于 PendingIntent 是存在于比當(dāng)前應(yīng)用更長生命周期的系統(tǒng)進(jìn)程中。這就是應(yīng)用進(jìn)程退出后,依然不影響消費 PendingIntent 的原因。

3.2 消費 PendingIntent 執(zhí)行過程

消費 PendingIntent 需要使用 PendingIntent#send() 方法,內(nèi)部會將創(chuàng)建 PendingIntent 時獲得的 Binder 對象 IIntentSender 發(fā)送給 AMS 服務(wù),用于執(zhí)行最終的 Intent 操作。關(guān)鍵源碼摘要如下:

PendingIntent.java

private final IIntentSender mTarget;

// 此處運行在應(yīng)用進(jìn)程
public void send(Context context, int code, @Nullable Intent intent, ...) throws CanceledException {
    if (sendAndReturnResult(context, code, intent, onFinished, handler, requiredPermission,options) < 0) {
        throw new CanceledException();
    }
}

public int sendAndReturnResult(Context context, int code, @Nullable Intent intent, ...) throws CanceledException {
        // 通過 Binder 通信執(zhí)行 IIntentSender
        return ActivityManager.getService().sendIntentSender(mTarget, mWhitelistToken, code, intent, resolvedType, ...);
}

ActivityManagerService.java

// 此處運行在 AMS 系統(tǒng)服務(wù)進(jìn)程
@Override
public int sendIntentSender(IIntentSender target, IBinder whitelistToken, int code, Intent intent, String resolvedType, ...) {
    if (target instanceof PendingIntentRecord) {
        return ((PendingIntentRecord)target).sendWithResult(code, intent, resolvedType, ...);
    }else {
        ...
    }
}

PendingIntentRecord.java

// 此處運行在 AMS 系統(tǒng)服務(wù)進(jìn)程
public int sendInner(int code, Intent intent, String resolvedType, ...) {
    // 此處處理以下標(biāo)記位的邏輯
    // FLAG_ONE_SHOT
    // FLAG_MUTABLE
    // FLAG_IMMUTABLE

    // FLAG_ONE_SHOT 標(biāo)記會移除 PendingIntentController 存儲的記錄
    if ((key.flags & PendingIntent.FLAG_ONE_SHOT) != 0) {
        controller.cancelIntentSender(this, true);
    }

    int res = START_SUCCESS;

    // 關(guān)鍵參數(shù):創(chuàng)建進(jìn)程的 UserId
    int userId = key.userId;

    switch (key.type) {
        case ActivityManager.INTENT_SENDER_ACTIVITY:
            res = controller.mAtmInternal.startActivitiesInPackage(
                uid /*關(guān)鍵參數(shù)*/, callingPid, callingUid, key.packageName, key.featureId,
                allIntents, allResolvedTypes, resultTo, mergedOptions, userId,
                false /* validateIncomingUser */,
                this /* originatingPendingIntent */,
                mAllowBgActivityStartsForActivitySender.contains(whitelistToken));
        break;

        case ActivityManager.INTENT_SENDER_ACTIVITY_RESULT:
        ...
        break;

        case ActivityManager.INTENT_SENDER_BROADCAST:
        ...
        break;

        case ActivityManager.INTENT_SENDER_SERVICE:
        case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE:
        ...
        break;
    }
    return res;
}

ActivityTaskManagerInternal.java

public abstract class ActivityTaskManagerInternal {
    public abstract int startActivityInPackage(int uid, int realCallingPid, int realCallingUid, ...);
}

ActivityTaskManagerInternal 是一個抽象類,小彭沒有找到其最終的實現(xiàn)類,有大佬知道的話請在評論區(qū)告訴我。

至此,就完成執(zhí)行 PendingIntent 中延遲操作的目的。 那么,為什么在當(dāng)前進(jìn)程執(zhí)行,還會以另一個進(jìn)程(PendingIntent 的創(chuàng)建進(jìn)程) 的身份執(zhí)行呢,關(guān)鍵在于使用了保存在 PendingIntentRecord 記錄中的 userId,這與我們通過常規(guī)的 Activity#startActivityAsUser() 是類似的。

Activity.java

@Override
public void startActivityAsUser(Intent intent, UserHandle user) {
    startActivityAsUser(intent, null, user);
}

4. 總結(jié)

到這里,PendingIntent 的內(nèi)容就講完了,相信你對 PendingIntent 的理解已經(jīng)超過絕大部分同學(xué),你認(rèn)同嗎?關(guān)注我,帶你了解更多,我們下次見。


參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容