標籤:原始碼 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 = 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
【從原始碼看Android】05 PendingIntent