通知借助PendingIntent启动Service
可以模拟这样一个场景,发送一个通知,然后将APP杀死,之后在通知栏通过PendingIntent启动Service,看看是否会出现禁止后台启动Service的场景。
void notify() {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
builder.setContentIntent(PendingIntent.getService(this, (int) System.currentTimeMillis(),
new Intent(this,
BackGroundService.class),
PendingIntent.FLAG_UPDATE_CURRENT))
.setContentText("content")...)
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT);
if (nm != null) {
nm.createNotificationChannel(channel);
}
}
nm.notify(1, builder.build());
}
实际结果是:点击通知后Service正常启动。下面逐步分析下。
同普通的Intent启动Service不同,这里的通知通过PendingIntent启动,是不是只要PendingIntent就足够了呢,并不是(后面分析)。通过通知启动Service的第一步是通过PendingIntent.getService获得一个用于启动特定Service的PendingIntent:
public static PendingIntent getService(Context context, int requestCode,
@NonNull Intent intent, @Flags int flags) {
return buildServicePendingIntent(context, requestCode, intent, flags,
ActivityManager.INTENT_SENDER_SERVICE);
}
private static PendingIntent buildServicePendingIntent(Context context, int requestCode,
Intent intent, int flags, int serviceKind) {
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
try {
intent.prepareToLeaveProcess(context);
IIntentSender target =
ActivityManager.getService().getIntentSender(
serviceKind, packageName,
null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
flags, null, context.getUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
IIntentSender在APP端其实是一个Binder代理,这里是典型的Binder双向通信模型,AMS端会为APP构建一个PendingIntentRecord extends IIntentSender.Stub实体, PendingIntentRecord可以看做PendingIntent在AMS端的记录,最终形成两者对应的双向通信通道。之后通知就会通过nm.notify显示在通知栏,这一步先略过,先看最后一步,通过点击通知启动Service,通知点击这不细看,只要明白最后调用的是PendingIntent的sendAndReturnResult函数,
public int sendAndReturnResult(Context context, int code, @Nullable Intent intent,
@Nullable OnFinished onFinished, @Nullable Handler handler,
@Nullable String requiredPermission, @Nullable Bundle options)
throws CanceledException {
try {
String resolvedType = intent != null ?
intent.resolveTypeIfNeeded(context.getContentResolver())
: null;
return ActivityManager.getService().sendIntentSender(
mTarget, mWhitelistToken, code, intent, resolvedType,
onFinished != null
? new FinishedDispatcher(this, onFinished, handler)
: null,
requiredPermission, options);
} catch (RemoteException e) {
throw new CanceledException(e);
}
}
通过Binder最终到AMS端,查找到对应的PendingIntentRecord,进入其sendInner函数,前文buildIntent的时候,用的是 ActivityManager.INTENT_SENDER_SERVICE,进入对应分支:
public int sendInner(int code, Intent intent, String resolvedType, IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo,
String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) {
if (whitelistDuration != null) {