首页 > 代码库 > 【从源代码看Android】05 PendingIntent
【从源代码看Android】05 PendingIntent
一、引入
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 = http://www.mamicode.com/Parcel.obtain();>
很明显,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
【从源代码看Android】05 PendingIntent