1.简介
Pending意思是待处理的、即将发生的,Intent则是Android中用于启动指定Activity、Service、Broadcast的意图信息类,连在一起表示一个将在未来某个时刻启动的意图,本质上是对Intent的封装。Android中PendingIntent主要用于拉起一个外部进程,或者不同进程间通信。具体场景是A进程作为发起端,它可以从系统“获取”一个PendingIntent,然后A进程可以将PendingIntent对象通过binder机制“传递”给B进程,再由B进程在未来某个合适时机,“回调”PendingIntent对象的send()动作,B进程可以通过PendingIntent携带额外信息通知或拉起已死亡的A进程的Activity、Service、Broadcast。
2.主要用途:传给外部应用用于通知自己或者拉起自己
- AlarmManager定时任务
- Notification通知点击
- Widget桌面卡片
- 应用间拉起(保活):两个APP可通过Binder传递自定义的PendingIntent对象,对方可在指定的某个逻辑时刻通知或拉起另一方,某些应用使用此特点进行业务进程保活。
3.使用
(1)获取PendingIntent API
PendingIntent.getActivity(...)
PendingIntent.getActivities(...)
PendingIntent.getBroadcast(...)
PendingIntent.getForegroundService(...)
PendingIntent.getService(Context, int, Intent, int)
(2)上述方法的flags参数
// 表示只能使用一次,使用后自动被cancel
public static final int FLAG_ONE_SHOT = 1<<30;
// 若新请求的PendingIntent发现已经存在,则继续使用已存在的,不存在则返回空
public static final int FLAG_NO_CREATE = 1<<29;
// 若新请求的PendingIntent已存在,则取消已存在的,并创建新PendingIntent替换
public static final int FLAG_CANCEL_CURRENT = 1<<28;
// 若新请求的PendingIntent已经存在,则都将被更新
public static final int FLAG_UPDATE_CURRENT = 1<<27;
Android12(Android S)及之后变更:PendingIntent必须声明可变性,即最后一个参数需带上新增的flag
// 表示PendingIntent对象在外部应用中不可被修改
public static final int FLAG_IMMUTABLE = 1<<26;
// PendingIntent.send()传入的intent内容可以被应用合并到PendingIntent中的 Intent
public static final int FLAG_MUTABLE = 1<<25;
(3)使用举例
A进程发送方 PendingIntent pendingIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
A进程通过AIDL发送pendingIntent给B进程,并实现对应intent的广播接收消息
B进程接收方获取到pendingIntent后,调用pendingIntent.send(context, code, intent)
即可触发A进程收到广播
(4)PendingIntent send方法
获取PendingIntent对象后通过调用send方法即可触发对应Intent意图,相当于startActivity()、startService()、sendBroadcast()
,
对于动态注册的广播也可收到。如果要取消 PendingIntent,可调用PendingIntent对象的cancel()
方法。
(5)PendingIntent匹配规则
如果两个PendingIntent内部的Intent相同并且requestCode也相同那么这两个PendingIntent相同。这里的Intent相同指ConponentName和intent-filter都相同,extras不参与匹配。即Intent.filterEquals的比较。
4.原理
A PendingIntent itself is simply a reference to a token maintained by the system describing the original data used to retrieve it.
通过PendingIntent的静态get方法获取到PendingIntent对象后,内部会通过Binder 通信将PendingIntent意图注册到AMS系统服务进程中,并获得一个Binder对象 IIntentSender封装在PendingIntent对象中返回。
PendingIntent.java
public static PendingIntent getBroadcast(Context context, int requestCode,
@NonNull Intent intent, @Flags int flags) {
return getBroadcastAsUser(context, requestCode, intent, flags, context.getUser());
}
public static PendingIntent getBroadcastAsUser(Context context, int requestCode,
Intent intent, int flags, UserHandle userHandle) {
String packageName = context.getPackageName();
String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver());
checkFlags(flags, packageName);
try {
intent.prepareToLeaveProcess(context);
IIntentSender target =
// Binder通信注册Intent到AMS
ActivityManager.getService().getIntentSenderWithFeature(
INTENT_SENDER_BROADCAST, packageName,
context.getAttributionTag(), null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
flags, null, userHandle.getIdentifier());
// 将获得的Binder对象IIntentSender封装在PendingIntent对象中返回
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
ActivityManagerService.java
// 运行在AMS
public IIntentSender getIntentSenderWithFeatureAsApp(int type, String packageName,
String featureId, IBinder token, String resultWho, int requestCode, Intent[] intents,
String[] resolvedTypes, int flags, Bundle bOptions, int userId, int owningUid) {
// NOTE: The service lock isn't held in this method because nothing in the method requires
// the service lock to be held.
...
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), owningUid, userId,
type == ActivityManager.INTENT_SENDER_BROADCAST,
ALLOW_NON_FULL, "getIntentSender", null);
...
return mPendingIntentController.getIntentSender(type, packageName, featureId,
owningUid, userId, token, resultWho, requestCode, intents, resolvedTypes,
flags, bOptions);
} catch (RemoteException e) {
throw new SecurityException(e);
}
}
PendingIntentController.java
// 通过HashMap保存PendingIntent
/** Set of IntentSenderRecord objects that are currently active. */
final HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> mIntentSenderRecords
= new HashMap<>();
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) {
synchronized (mLock) {
if (DEBUG_MU) Slog.v(TAG_MU, "getIntentSender(): uid=" + callingUid);
// We're going to be splicing together extras before sending, so we're
// okay poking into any contained extras.
if (intents != null) {
for (int i = 0; i < intents.length; i++) {
intents[i].setDefusable(true);
}
}
Bundle.setDefusable(bOptions, true);
final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
final boolean cancelCurrent = (flags & PendingIntent.FLAG_CANCEL_CURRENT) != 0;
final boolean updateCurrent = (flags & PendingIntent.FLAG_UPDATE_CURRENT) != 0;
flags &= ~(PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_CANCEL_CURRENT
| PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, featureId,
token, resultWho, requestCode, intents, resolvedTypes, flags,
SafeActivityOptions.fromBundle(bOptions), userId);
...
rec = new PendingIntentRecord(this, key, callingUid);
mIntentSenderRecords.put(key, rec.ref);
incrementUidStatLocked(rec);
return rec;
}
}
其它进程需要触发延时意图时通过PendingIntent#send()
public int sendAndReturnResult(Context context, int code, @Nullable Intent intent,
@Nullable OnFinished onFinished, @Nullable Handler handler,
@Nullable String requiredPermission, @Nullable Bundle options)
throws CanceledException {
...
return ActivityManager.getService().sendIntentSender(
mTarget, mWhitelistToken, code, intent, resolvedType,
onFinished != null
? new FinishedDispatcher(this, onFinished, handler)
: null,
requiredPermission, options);
}
ActivityManagerService.java
最终会通过AMS拉起对应组件
// 运行在AMS
public int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code,
Intent intent, String resolvedType,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
if (target instanceof PendingIntentRecord) {
return ((PendingIntentRecord)target).sendWithResult(code, intent, resolvedType,
allowlistToken, finishedReceiver, requiredPermission, options);
} else {
...
}
5.安全性
上述操作A进程给B进程发送PendingIntent对象,B进程可通过此拉起A进程,操作上看起来也可通过自己封装Intent对象传递给B进程。这里PendingIntent除了做Intent这样的封装外,通过AMS将A进程的进程id也存在了AMS中,当B进程PendingIntent.send拉起A进程时,走到AMS实际体现在以A进程的身份拉起自己,通过AMS控制从而保证了拉起A进程组件的安全性,从而A进程的组件不用设置为exported也能被拉起,故不用外部暴露。
6.总结
PendingIntent的优势主要体现在延迟拉起Activity、Service、Broadcast,即使对方进程已死亡,可用于拉起对方进程进行业务,完成预定操作,如拉起下拉通知对应的Activity或通过AlarmManager拉起一个定时闹钟页面,同时保证了拉起操作的安全性。
参考:
PendingIntent官网介绍
Android PendingIntent
关于PendingIntent需要知道的事
PendingIntent和Intent区别
网友评论