# PendingIntent使用解析 站点:答源(https://www.dandroid.cn/) 原文:https://www.dandroid.cn/archives/20807 Canonical:https://www.dandroid.cn/archives/20807 作者:admin 发布日期:2023-09-22 最后更新:2023-09-22 06:58 分类:教程 内容类型:post 说明:本页是原文的纯文本镜像,供大模型与检索器完整读取;不含导航、广告、付费框。引用请以上方「原文」链接为准。 ## 正文 PendingIntent 中的单词 “pending” 指延迟或挂起,就是指它是延迟的或挂起的。一句话概括,PendingIntent 一种是支持授权其他应用以当前应用的身份执行包装的 Intent 操作的系统特性。 什么场景下会使用 PendingIntent? 场景 1 - 系统通知消息的点击操作 场景 2 - 桌面Widget的点击操作 场景 3 - 系统闹钟操作 场景 4 - 第三方应用回调操作 PendingIntent 和 Intent 有什么区别? 从结构上来说,PendingIntent 是 Intent 的包装类,其内部持有一个代表最终意图操作的 Intent。 1、执行进程不同 —— PendingIntent 在其他进程执行: Intent 通常会在创建进程中执行,而 PendingIntent 通常不会在创建进程中执行; 2、执行时间不同 —— PendingIntent 会延迟执行: Intent 通常会立即执行,而 PendingIntent 通常会延迟执行,延迟到其他进程完成任务后再执行,甚至延迟到创建进程消亡后。例如,在 场景 1 - 系统通知消息的点击操作 中,即使发送系统通知消息的进程已经消亡了,依然不妨碍二级 Intent 的跳转; 3、执行身份不同 —— PendingIntent 支持授权: PendingIntent 内部持有授权信息,支持其他应用以当前应用的身份执行,这有利于避免嵌套 Intent 存在的安全隐患。 PendingIntent 的使用方法 启动组件: // 启动 Activity PendingIntent.getActivity(Context context, int requestCode, Intent intent, int flags) // 启动 Service PendingIntent.Service(Context context, int requestCode, Intent intent, int flags) // 发送广播 PendingIntent.getBroadcast(Context context, int requestCode, Intent intent, int flags) PendingIntent 的 4 个参数: 1、context: 当前应用的上下文,PendingIntent 将从中抽取授权信息; 2、requestCode: PendingIntent 的请求码,与 Intent 的请求码类似; 3、intent: 需要跳转的包装intent; 4、flag: PendingIntent标记位 PendingIntent标记位 FLAG_IMMUTABLE:不可变标记位,将约束外部应用消费 PendingIntent 修改其中的 Intent; FLAG_MUTABLE:可变标记位,不约束外部应用消费 PendingIntent 修改其中的 Intent; FLAG_UPDATE_CURRENT:如果系统中已经存在相同的 PendingIntent,那么将保留原有 PendingIntent 对象,而更新其中的 Intent。即使不可变 PendingIntent,依然可以在当前应用更新; FLAG_CANCEL_CURRENT:如果系统中已经存在相同的 PendingIntent,那么将先取消原有的 PendingIntent,并重新创建新的 PendingIntent。 FLAG_NO_CREATE:如果系统中已经存在相同的 PendingIntent,那么不会重新创建,而是直接返回 null; FLAG_ONE_SHOT:一次有效标记位,PendingIntent 被消费后不支持重复消费,即只能使用一次。 PendingIntent 可变性是一种对外部应用消费行为的约束机制,通过标记位 FLAG_MUTABLE 和 FLAG_IMMUTABLE 控制 PendingIntent 可变或不可变。可变性意味着在消费 PendingIntent 时,可以针对其中包装的 Intent 进行修改。强烈建议在创建 PendingIntent 时使用 FLAG_IMMUTABLE。 完整使用示例: val intent = Intent() intent.action = "xxxaction" intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP var flag = PendingIntent.FLAG_UPDATE_CURRENT if (android.os.Build.VERSION.SDK_INT >= 31) { flag = flag or PendingIntent.FLAG_IMMUTABLE } PendingIntent.getActivity( context, appWidgetId, intent, flag ) PendingIntent 延后启动 Activity/Service/Broadcast分析 省流: client 封装真实intent到 PendingIntent -> 真正执行时 PendingIntent.send -> PendingIntentRecord.send & sendInner -> ATMS.startActivityInPackage/startActivitiesInPackage/sendActivityResult/broadcastIntentInPackage/startServiceInPackage 即 xxxInPackage 1. 概念 PendingIntent:Intent的封装。 把这个封装好的intent交给别的程序, 别的程序根据这个intent延后处理intent中所描述的事情。 2. 三个方法: getActivity(Context context, int requestCode, Intent intent, int flags, Bundle options) getBroadcast(Context context, int requestCode, Intent intent, int flags) getService(Context context, int requestCode, Intent intent, int flags) 3. PendingIntentRecord 向AMS 发起启动请求 最后通过 PendingIntentRecord 向AMS 发起启动请求, 调用AMS的对应方法 xxxInPackage(如startActivityInPackage) 源码: frameworks/base/services/core/java/com/android/server/am/PendingIntentRecord.java public int sendInner(int code, Intent intent, String resolvedType, IBinder allowlistToken, IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo, String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) { switch (key.type) { case ActivityManager.INTENT_SENDER_ACTIVITY: ... if (key.allIntents != null && key.allIntents.length > 1) { res = controller.mAtmInternal.startActivitiesInPackage( ... } else { res = controller.mAtmInternal.startActivityInPackage(uid, callingPid, ... case ActivityManager.INTENT_SENDER_BROADCAST: int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName, ... case ActivityManager.INTENT_SENDER_SERVICE: case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE: controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType, 其中: controller 是 PendingIntentController 对象,它的构造函数里,会去获取倒 ATMS 的本地代理对象, 即 ActivityTaskManagerInternal 对象 PendingIntentController(Looper looper, UserController userController, ActivityManagerConstants constants) { mH = new Handler(looper); mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); mUserController = userController; mConstants = constants; } ActivityTaskManagerInternal 为 ATMS 的本地接口(system_server进程内部使用) /** * Activity Task manager local system service interface. * @hide Only for use within system server */ public abstract class ActivityTaskManagerInternal { ...} ActivityTaskManagerService 内部类 LocalService 实现了 ActivityTaskManagerInternal的接口, 并把LocalService 的对象添加到 LocalServices。 public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public ActivityTaskManagerService(Context context) { ... mInternal = new LocalService(); ... public ActivityTaskManagerInternal getAtmInternal() { return mInternal; } private void start() { LocalServices.addService(ActivityTaskManagerInternal.class, mInternal); } final class LocalService extends ActivityTaskManagerInternal { @Override public int startActivityInPackage(int uid, int realCallingPid, int realCallingUid..) { return getActivityStartController().startActivityInPackage(...) } @Override public int startActivityAsUser(IApplicationThread caller....) { return ActivityTaskManagerService.this.startActivityAsUser( } 因此, system_server 其它地方则可以通过 LocalServices.getService(ActivityTaskManagerInternal.class) 去获取到 ActivityTaskManagerInternal 的对象, 进而访问到 ATMS 或者直接处理。 4.参考: PendingIntent源码分析: https://blog.csdn.net/konan1027/article/details/46914715 PendingIntentRecord 源码: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/am/PendingIntentRecord.java?hl=zh-cn Android中pendingIntent的深入理解:https://blog.csdn.net/yuzhiboyi/article/details/8484771 Intent和PendingIntent的区别 a. Intent是立即使用的,而PendingIntent可以等到事件发生后触发,PendingIntent可以cancel b. Intent在程序结束后即终止,而PendingIntent在程序结束后依然有效 c. PendingIntent自带Context,而Intent需要在某个Context内运行 d. Intent在原task中运行,PendingIntent在新的task中运行 5. 一个PendingIntent 执行的调用栈: Client 调用AMS.sendIntentSender -> PendingIntentRecord.sendInner -> ActivityTaskManagerService.startActivityInPackage ->ActivityStartController.startActivityInPackage -> ActivityStarter.execute/executeRequest 则会执行 start u 07-06 18:49:42.134 E 1876 5896 at com.android.server.wm.ActivityStarter.executeRequest(ActivityStarter.java:1197) 07-06 18:49:42.134 E 1876 5896 at com.android.server.wm.ActivityStarter.execute(ActivityStarter.java:1026) 07-06 18:49:42.134 E 1876 5896 at com.android.server.wm.ActivityStartController.startActivityInPackage(ActivityStartController.java:425) 07-06 18:49:42.134 E 1876 5896 at com.android.server.wm.ActivityTaskManagerService$LocalService.startActivityInPackage(ActivityTaskManagerService.java:6926) 07-06 18:49:42.134 E 1876 5896 at com.android.server.am.PendingIntentRecord.sendInner(PendingIntentRecord.java:586) 07-06 18:49:42.134 E 1876 5896 at com.android.server.am.ActivityManagerService.sendIntentSender(ActivityManagerService.java:6579) 07-06 18:49:42.134 E 1876 5896 at com.android.server.am.ActivityManagerService.sendIntentSender(ActivityManagerService.java:6568) 07-06 18:49:42.134 E 1876 5896 at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:4856) 07-06 18:49:42.134 E 1876 5896 at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3135) 07-06 18:49:42.134 E 1876 5896 at android.os.Binder.execTransactInternal(Binder.java:1316) 07-06 18:49:42.134 E 1876 5896 at android.os.Binder.execTransact(Binder.java:1280) 6. 获取PendingIntent 对象 源码 以获取Activity 的PI 对象为例, 可见有两个方法,分别为 getActivity 和 getActivityAsUser, 前者是公开,后者是hide的。 后者可以传入 当前 user。 前者默认的user 是通过传入的 context 去获取的。 @SuppressWarnings("AndroidFrameworkPendingIntentMutability") public static PendingIntent getActivity(Context context, int requestCode, @NonNull Intent intent, @Flags int flags, @Nullable Bundle options) { // Some tests only mock Context.getUserId(), so fallback to the id Context.getUser() is null final UserHandle user = context.getUser(); return getActivityAsUser(context, requestCode, intent, flags, options, user != null ? user : UserHandle.of(context.getUserId())); } /** * @hide * Note that UserHandle.CURRENT will be interpreted at the time the * activity is started, not when the pending intent is created. */ @UnsupportedAppUsage public static PendingIntent getActivityAsUser(Context context, int requestCode, @NonNull Intent intent, int flags, Bundle options, UserHandle user) { String packageName = context.getPackageName(); String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver()); checkFlags(flags, packageName); try { intent.migrateExtraStreamToClipData(context); intent.prepareToLeaveProcess(context); IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature( INTENT_SENDER_ACTIVITY, packageName, context.getAttributionTag(), null, null, requestCode, new Intent[] { intent }, resolvedType != null ? new String[] { resolvedType } : null, flags, options, user.getIdentifier()); return target != null ? new PendingIntent(target) : null; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } --- 由 答源 DAN 底座 1.4.1 生成 · 2026-11-15T02:17:18+00:00