一、引入
PendingIntent是一个非常不起眼的类,
你可能在以下情况下遇到过它
1、AlarmManager
int requestID = 1;
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
Intent i = new Intent(this,AshqalReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(this
,requestID
,i
,PendingIntent.FLAG_UPDATE_CURRENT);
am.setRepeating(AlarmManager.RTC_WAKEUP
, System.currentTimeMillis() + 1000
, 1000
, pi);
2、NotificationManager
//初始化
int requestID = 1;
mPendingIntent = PendingIntent.getActivity(this
,requestID
,new Intent(this,MyActivity.class)
,PendingIntent.FLAG_UPDATE_CURRENT);
//调用
NotificationManager nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentTitle("Notification Title");
builder.setContentText("context text");
builder.setContentIntent(mPendingIntent);
//builder.setAutoCancel(true);
Notification notification = builder.build();
nm.notify(0,notification);
或者
PendingIntent mPendingIntent = PendingIntent.getService(mCtx
,requestID
,mIntent
,PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(mPendingIntent);
2种方式都是将想要的操作(调用broadcast、打开activity、打开service)保存到PendingIntent中,在合适的时候执行这个操作
不管从下拉消息栏打开activity或者打开service操作,还是通过AlarmManager定时广播的方式,
都是跨进程的方式下进行的,当前的PendingIntent对象都保存在了哪?如何在其他进程中调用到PendingIntent?
这得从PendingIntent的创建开始讲
二、PendingIntent创建
前面提到了3种静态的方式创建,按照android sdk reference里的描述,分别是
PendIntent.getService,PendingIntent.getActivity,PendingIntent.getBroadcast
这三种创建方法很类似,我们只挑选其中PendIntent.getService进行
创建示例代码如下:
PendingIntent mPendingIntent = PendingIntent.getService(mCtx
,requestID
,mIntent
,PendingIntent.FLAG_UPDATE_CURRENT);
如果需要之后的操作是startService,那么就需要从PendingIntent.getService创建PendingIntent
//android.app.PendingIntent.java,getService函数实现
public static PendingIntent getService(Context context, int requestCode,
Intent intent, int flags) {
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
try {
intent.prepareToLeaveProcess();
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender(
ActivityManager.INTENT_SENDER_SERVICE, packageName,
null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
flags, null, UserHandle.myUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
return null;
}
从context获取到包名和Intent的MIME类型(如text/plain),
使用静态函数ActivityManagerNative.getDefault() 获取到ActivityManagerService的本地代理对象ActivityManagerProxy(其中封装了远程的ActivityManagerService)
调用这个ActivityManagerProxy的getIntentSender函数
//android.app.ActivityManagerNative.ActivityManagerProxy
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
Bundle options, int userId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(type);
data.writeString(packageName);
data.writeStrongBinder(token);
data.writeString(resultWho);
data.writeInt(requestCode);
if (intents != null) {
data.writeInt(1);
data.writeTypedArray(intents, 0);
data.writeStringArray(resolvedTypes);
} else {
data.writeInt(0);
}
data.writeInt(flags);
if (options != null) {
data.writeInt(1);
options.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
data.writeInt(userId);
mRemote.transact(GET_INTENT_SENDER_TRANSACTION, data, reply, 0);
reply.readException();
IIntentSender res = IIntentSender.Stub.asInterface(
reply.readStrongBinder());
data.recycle();
reply.recycle();
return res;
}
很明显,ActivityManagerProxy将一系列参数数据序列化到一个Parcel对象,然后调用mRemote(远程的ActivityManagerService对象)的transact方法,
告诉在System进程的ActivityManagerService处理这个事物
在System进程的ActivityManagerService处理这个事物代码
//android.app.ActivityManagerNative.onTransact方法片段,
//因为ActivityManagerService继承自ActivityManagerNative,
//ActivityManagerService将此请求放到ActivityManagerNative处理
case GET_INTENT_SENDER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int type = data.readInt();
String packageName = data.readString();
IBinder token = data.readStrongBinder();
String resultWho = data.readString();
int requestCode = data.readInt();
Intent[] requestIntents;
String[] requestResolvedTypes;
if (data.readInt() != 0) {
requestIntents = data.createTypedArray(Intent.CREATOR);
requestResolvedTypes = data.createStringArray();
} else {
requestIntents = null;
requestResolvedTypes = null;
}
int fl = data.readInt();
Bundle options = data.readInt() != 0
? Bundle.CREATOR.createFromParcel(data) : null;
int userId = data.readInt();
IIntentSender res = getIntentSender(type, packageName, token,
resultWho, requestCode, requestIntents,
requestResolvedTypes, fl, options, userId);
reply.writeNoException();
reply.writeStrongBinder(res != null ? res.asBinder() : null);
return true;
}
将data反序列化获得数据,调用ActivityManagerService的getIntentSender方法,然后把回馈写入reply,返回从mRemote.transcat函数返回
我们深入分析下ActivityManagerService的getIntentSender方法,如下
@Override
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes,
int flags, Bundle options, int userId) {
enforceNotIsolatedCaller("getIntentSender");
// Refuse possible leaked file descriptors
if (intents != null) {
if (intents.length < 1) {
throw new IllegalArgumentException("Intents array length must be >= 1");
}
for (int i=0; i<intents.length; i++) {
Intent intent = intents[i];
if (intent != null) {
if (intent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
if (type == ActivityManager.INTENT_SENDER_BROADCAST &&
(intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
throw new IllegalArgumentException(
"Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
}
intents[i] = new Intent(intent);
}
}
if (resolvedTypes != null && resolvedTypes.length != intents.length) {
throw new IllegalArgumentException(
"Intent array length does not match resolvedTypes length");
}
}
if (options != null) {
if (options.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in options");
}
}
synchronized(this) {
int callingUid = Binder.getCallingUid();
int origUserId = userId;
userId = handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
type == ActivityManager.INTENT_SENDER_BROADCAST, false,
"getIntentSender", null);
if (origUserId == UserHandle.USER_CURRENT) {
// We don't want to evaluate this until the pending intent is
// actually executed. However, we do want to always do the
// security checking for it above.
userId = UserHandle.USER_CURRENT;
}
try {
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
int uid = AppGlobals.getPackageManager()
.getPackageUid(packageName, UserHandle.getUserId(callingUid));
if (!UserHandle.isSameApp(callingUid, uid)) {
String msg = "Permission Denial: getIntentSender() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ ", (need uid=" + uid + ")"
+ " is not allowed to send as package " + packageName;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
}
return getIntentSenderLocked(type, packageName, callingUid, userId,
token, resultWho, requestCode, intents, resolvedTypes, flags, options);
} catch (RemoteException e) {
throw new SecurityException(e);
}
}
}
首先深拷贝了整组intent,
然后通过传递过来的信息检查callingUid和packageName对应的uid是否相同,
然后调用到函数getIntentSendrLocked中
//ActivityManagerService的getIntentSendrLocked方法
IIntentSender getIntentSenderLocked(int type, String packageName,
int callingUid, int userId, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
Bundle options) {
if (DEBUG_MU)
Slog.v(TAG_MU, "getIntentSenderLocked(): uid=" + callingUid);
ActivityRecord activity = null;
if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
activity = ActivityRecord.isInStackLocked(token);
if (activity == null) {
return null;
}
if (activity.finishing) {
return null;
}
}
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, activity, resultWho,
requestCode, intents, resolvedTypes, flags, options, userId);
WeakReference<PendingIntentRecord> ref;
ref = mIntentSenderRecords.get(key);
PendingIntentRecord rec = ref != null ? ref.get() : null;
if (rec != null) {
if (!cancelCurrent) {
if (updateCurrent) {
if (rec.key.requestIntent != null) {
rec.key.requestIntent.replaceExtras(intents != null ?
intents[intents.length - 1] : null);
}
if (intents != null) {
intents[intents.length-1] = rec.key.requestIntent;
rec.key.allIntents = intents;
rec.key.allResolvedTypes = resolvedTypes;
} else {
rec.key.allIntents = null;
rec.key.allResolvedTypes = null;
}
}
return rec;
}
rec.canceled = true;
mIntentSenderRecords.remove(key);
}
if (noCreate) {
return rec;
}
rec = new PendingIntentRecord(this, key, callingUid);
mIntentSenderRecords.put(key, rec.ref);
if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
if (activity.pendingResults == null) {
activity.pendingResults
= new HashSet<WeakReference<PendingIntentRecord>>();
}
activity.pendingResults.add(rec.ref);
}
return rec;
}
这里使用到了刚刚参数中的3个Flags
FLAG_NO_CREATE,不存在就创建,存在就删除后再创建,一次性使用,不会放到mIntentSenderRecords中
FLAG_CANCEL_CURRENT,存在就删除当前的,再创建新的放入mIntentSenderRecords中
FLAG_UPDATE_CURRENT,存在且不为cancel则替换intent,否则创建新的,放入mIntentSenderRecords中
读取到值后将三个flag关闭
之后用传递过来的多个参数组成一个PendingIntentRecord.Key,然后在mIntentSenderRecords容器中寻找这个key是否存在
按上述3个Flag逻辑执行
最后都返回一个PendingIntentRecord,
这个PendingIntentRecord继承自aidl接口IIntentSender.Stub,实现了send方法
最后把获取到的PendingIntentRecord通过序列化方式存到reply中,
通过binder机制进程间通讯,把数据回传给用户空间
使用PendingIntent封装这个IIntentSender,new PendingIntent(IIntentSender)
这样PendingIntent就创建完成了
因为PendingIntentRecord.Key重写了自己的hashCode和equals方法,
所以即使对象的PendingIntent内存地址不同,也有可能被判定为相同的请求
二、PendingIntent的使用
因为把PendingIntent对象传递给了目标(如顶栏通知栏、广播),
如在AlarmManager中把传递给AlarmManager的PendingIntent对象封装成了Alarm对象
Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval,
operation, workSource);
然后在恰当的时机对PendingIntent对象调用send方法
//com.android.server.AlarmManagerService.AlarmHander.handlerMessage
public void handleMessage(Message msg) {
if (msg.what == ALARM_EVENT) {
ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
synchronized (mLock) {
final long nowRTC = System.currentTimeMillis();
final long nowELAPSED = SystemClock.elapsedRealtime();
triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
}
// now trigger the alarms without the lock held
for (int i=0; i<triggerList.size(); i++) {
Alarm alarm = triggerList.get(i);
try {
alarm.operation.send();
} catch (PendingIntent.CanceledException e) {
if (alarm.repeatInterval > 0) {
// This IntentSender is no longer valid, but this
// is a repeating alarm, so toss the hoser.
remove(alarm.operation);
}
}
}
}
}
就可以触发PendingIntentRecoard的sendInner函数,
例如类型为service的,调用如下函数片段
case ActivityManager.INTENT_SENDER_SERVICE:
try {
owner.startServiceInPackage(uid,
finalIntent, resolvedType, userId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Unable to send startService intent", e);
}
break;
startServiceInPackage,这样就可以启动service了
四、启发
受此启发,因为PendingIntent继承自Parcelable可以被序列化
我们只要把PendingIntent通过Intent序列化给其他相同package的组件中,
反序列化回来,调用send,就能触发预定义的操作
具体方式参见:how-to-use-pendingintent-to-communicate-from-a-service-to-a-client-activity
http://stackoverflow.com/questions/6099364/how-to-use-pendingintent-to-communicate-from-a-service-to-a-client-activity