【從原始碼看Android】05 PendingIntent

來源:互聯網
上載者:User

標籤:原始碼   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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.