关于Notification的一点愚见(PendingIntent工作流程)

本文深入解析了PendingIntent的概念及其与Intent的区别,并详细介绍了PendingIntent在Android系统中的实现原理,包括其声明方式、工作流程及如何通过NotificationManager触发。

PendingIntent,顾名思义,延迟的Intent。那么肯定会产生一个疑问,那就是PendingIntent究竟和Intent的区别在哪里呢?
其实PendingIntent其实就是对Intent的一个封装,不过Intent是立即触发效果,就像我们常用的这样:

        Intent intent = new Intent(MainActivity.this,DemoActivity1.class);
        startActivity(intent);

而PendingIntent触发的时间点不太一样,一般是触发效果的地点不在函数中,而是在其他方法处被调用,所以看起来就像被延迟了一样。

PendingIntent的声明

我们可以知道PendingIntent可以支持三种意图:启动Activity,启动Service,启动Broadcast。我们取出其中一个场景来讨论,其他两个场景是类似的。
上一章中,有如下这段:

PendingIntent pendingIntent = PendingIntent.getActivity(this,
                    0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

这一段就是PendingIntent的声明,调用PendingIntent中的getActivity()的方法返回实例。
下面是getActivity的源码:

    public static PendingIntent getActivity(Context context, int requestCode,
            Intent intent, @Flags int flags) {
        return getActivity(context, requestCode, intent, flags, null);
    }

这里又调用了:

public static PendingIntent getActivity(Context context, int requestCode,
            @NonNull Intent intent, @Flags int flags, @Nullable Bundle options) {
        String packageName = context.getPackageName();
        String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
                context.getContentResolver()) : null;
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess();
            IIntentSender target =
                ActivityManagerNative.getDefault().getIntentSender(
                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
                    null, null, requestCode, new Intent[] { intent },
                    resolvedType != null ? new String[] { resolvedType } : null,
                    flags, options, UserHandle.myUserId());
            return target != null ? new PendingIntent(target) : null;
        } catch (RemoteException e) {
        }
        return null;
    }

其中核心代码:

IIntentSender target =
                ActivityManagerNative.getDefault().getIntentSender(
                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
                    null, null, requestCode, new Intent[] { intent },
                    resolvedType != null ? new String[] { resolvedType } : null,
                    flags, options, UserHandle.myUserId());
            return target != null ? new PendingIntent(target) : null;

而这里ActivityManagerNative.getDefault()是指的是AMS(ActivityManagerService)这个类,这个类可以说是贯穿了Android整个应用层的结构。它在Android系统中,四大组件的行为和它密切相关。
而AMS是属于原进程之外,那么我们就必须完成进程间通讯的动作。而同时Activity是继承了Binder实现了IActivityManager的接口。之前的讲述Binder的时候也说过了,在Android的进程间通讯,Binder起到了至关重要的作用。

于此同时,也要说明一下IIntentSender这个类其实是继承了IInterface抽象类的接口。

 public interface IIntentSender extends android.os.IInterface

AMS中PendingIntent的工作

我们下去AMS的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) {

....//获取Intent对象,存入数组并且对非法的intent做出处理

        synchronized(this) {
            int callingUid = Binder.getCallingUid();
            int origUserId = userId;
            userId = handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
                    type == ActivityManager.INTENT_SENDER_BROADCAST,
                    ALLOW_NON_FULL, "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);
            }
        }
    }

我们主要看这一段线程同步的代码,最后你会发现获取完uid之类的信息之后就会回调函数getIntentSenderLocked(type, packageName, callingUid, userId,
token, resultWho, requestCode, intents, resolvedTypes, flags, options);这个才是真正处理问题的函数。

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;
    }

这一段做的工作有一下几点:
1.假若flags是type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT。则说明是从Activity应答的PendingIntent,我们不做讨论。

2.假如PendingIntentRecord不为空,根据PendingIntent的标志位,Intent数组的做出调整来达到PendingIntent的效果。
这里先说一下PendingIntent,有四个标志位:

1)FLAG_ONE_SHOT :当前描述的PendingIntent只能被使用一次,然后它就会调用cancel删除。如果还有后续相同的PendingIntent,那么他们的send方法将会调用失败。
对于通知栏而言,采用此标志位,同类通知只能使用一次,后续点击通知会无效。

2)FLAG_NO_CREATE:当前描述的PendingIntent不会主动创建,如果当前PendingIntent之前不存在,则所有的getActivity,getService,getBroadcast返回null,获取PendingIntent失败。这个很少用,暂时不讨论。

3)FLAG_CANCEL_CURRENT:当前描述的PendingIntent已经存在了,那么他们会被cancel,然后系统创建一个新的PendingIntent。对于通知栏而言,被cancel后的消息单击无效。

4)FLAG_UPDATE_CURRENT:如果当前的PendingIntent已经存在,则它们都会被更新,即里面的PendingIntent都换为最新。

3.假如PendingIntentRecord为空则创建一个新的PendingIntentRecord。并且存入Activity中PendingIntentRecord的HashSet中。最后返回PendingIntentRecord。

这个时候,我们发现返回了PendingIntentRecord,说明这个类正是实现了IIntentSender。让我们看看这个类是怎么回事吧。

final class PendingIntentRecord extends IIntentSender.Stub

看到了吧,这个类正是实现了IIntentSender.Stub的类。

final class PendingIntentRecord extends IIntentSender.Stub

是不是觉得和眼熟呢?没错就是我们之前分析的AIDL,在这个过程AMS相当于中转器,就像设计模式中的中介者一样。将函数转移到远程端的实现。这种思想在探索四大组件的时候经常会用到。

PendingIntentRecord中工作

在这个类中完成的事情只有一件,那就是将数据存储进来。

PendingIntentRecord(ActivityManagerService _owner, Key _k, int _u) {
        owner = _owner;
        key = _k;
        uid = _u;
        ref = new WeakReference<PendingIntentRecord>(this);
    }

//内部类key的定义
     Key(int _t, String _p, ActivityRecord _a, String _w,
                int _r, Intent[] _i, String[] _it, int _f, Bundle _o, int _userId) {
            type = _t;
            packageName = _p;
            activity = _a;
            who = _w;
            requestCode = _r;
            requestIntent = _i != null ? _i[_i.length-1] : null;
            requestResolvedType = _it != null ? _it[_it.length-1] : null;
            allIntents = _i;
            allResolvedTypes = _it;
            flags = _f;
            options = _o;
            userId = _userId;

            int hash = 23;
            hash = (ODD_PRIME_NUMBER*hash) + _f;
            hash = (ODD_PRIME_NUMBER*hash) + _r;
            hash = (ODD_PRIME_NUMBER*hash) + _userId;
            if (_w != null) {
                hash = (ODD_PRIME_NUMBER*hash) + _w.hashCode();
            }
            if (_a != null) {
                hash = (ODD_PRIME_NUMBER*hash) + _a.hashCode();
            }
            if (requestIntent != null) {
                hash = (ODD_PRIME_NUMBER*hash) + requestIntent.filterHashCode();
            }
            if (requestResolvedType != null) {
                hash = (ODD_PRIME_NUMBER*hash) + requestResolvedType.hashCode();
            }
            hash = (ODD_PRIME_NUMBER*hash) + _p.hashCode();
            hash = (ODD_PRIME_NUMBER*hash) + _t;
            hashCode = hash;
            //Slog.i(ActivityManagerService.TAG, this + " hashCode=0x"
            //        + Integer.toHexString(hashCode));
        }

在这一段代码中,很好的解释了为什么我们在声明PendingIntent的时候,请求码一般为0,在上面hashcode的数值和请求码requestCode进行相加的运算获得新的请求码。假如请求码变动了,hashcode也变动了,那么在AMS中建立的HashSet的检索策略不同了就会造成寻找到的Intent不同,对错误的Intent处理了错误标志位。

到这里,PendingIntent已经存储,完毕正在等待用户的调度。

NotificationManager的调用

我们这里分析Notification中PendingIntent的情况,其实在AppWidget中调用PendingIntent的情景也是十分类似。

我们在制作通知的时候,最后会声明一个NotificationManager的notify去唤醒PendingIntent并且执行。

让我们去NotificationManager中看看,notify方法吧。
我们可以知道, public void notify(int id, Notification notification)又会调用 public void notify(String tag, int id, Notification notification)

 public void notify(String tag, int id, Notification notification)
    {
        int[] idOut = new int[1];
        INotificationManager service = getService();
        String pkg = mContext.getPackageName();
        if (notification.sound != null) {
            notification.sound = notification.sound.getCanonicalUri();
            if (StrictMode.vmFileUriExposureEnabled()) {
                notification.sound.checkFileUriExposed("Notification.sound");
            }
        }
        if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
        Notification stripped = notification.clone();
        Builder.stripForDelivery(stripped);
        try {
            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
                    stripped, idOut, UserHandle.myUserId());
            if (id != idOut[0]) {
                Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
            }
        } catch (RemoteException e) {
        }
    }

在这一段中,我们只用注意这个函数service.enqueueNotificationWithTag(pkg, mContext.getOpPack-ageName(), tag, id,stripped, idOut, UserHandle.myUserId());
这个函数也是进程间的通讯,透过INotificationManager这个AIDL实现的类,远程调用NotificationManagerService中方法。这个过程也是AIDL实现的Binder弯完成的。

NotificationManagerService中的工作流程

在这里,声明了一个IBinder来实现Stub:

private final IBinder mService = new INotificationManager.Stub() 

而函数enqueueNotificationWithTag又会调用:

void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
            final int callingPid, final String tag, final int id, final Notification notification,
            int[] idOut, int incomingUserId) {
            ...
            //从NotificationList中获取通知存入NotificationRecord,以及对Notification中
            //的一些非法额度参数进行异常处理
            mHandler.post(new Runnable() {
            @Override
            public void run() {

                synchronized (mNotificationList) {

                    // === Scoring ===

                    // 0. Sanitize inputs
                    notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
                            Notification.PRIORITY_MAX);
                    // Migrate notification flags to scores
                    if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
                        if (notification.priority < Notification.PRIORITY_MAX) {
                            notification.priority = Notification.PRIORITY_MAX;
                        }
                    } else if (SCORE_ONGOING_HIGHER &&
                            0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
                        if (notification.priority < Notification.PRIORITY_HIGH) {
                            notification.priority = Notification.PRIORITY_HIGH;
                        }
                    }

                    // 1. initial score: buckets of 10, around the app [-20..20]
                    final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;

                    // 2. extract ranking signals from the notification data
                    final StatusBarNotification n = new StatusBarNotification(
                            pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
                            user);
                    NotificationRecord r = new NotificationRecord(n, score);
                    NotificationRecord old = mNotificationsByKey.get(n.getKey());
                    if (old != null) {
                        // Retain ranking information from previous record
                        r.copyRankingInformation(old);
                    }
                    mRankingHelper.extractSignals(r);

                    // 3. Apply local rules

                    // blocked apps
                    if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
                        if (!isSystemNotification) {
                            r.score = JUNK_SCORE;
                            Slog.e(TAG, "Suppressing notification from package " + pkg
                                    + " by user request.");
                        }
                    }

                    if (r.score < SCORE_DISPLAY_THRESHOLD) {
                        // Notification will be blocked because the score is too low.
                        return;
                    }

                    // Clear out group children of the old notification if the update causes the
                    // group summary to go away. This happens when the old notification was a
                    // summary and the new one isn't, or when the old notification was a summary
                    // and its group key changed.
                    if (old != null && old.getNotification().isGroupSummary() &&
                            (!notification.isGroupSummary() ||
                                    !old.getGroupKey().equals(r.getGroupKey()))) {
                        cancelGroupChildrenLocked(old, callingUid, callingPid, null);
                    }

                    int index = indexOfNotificationLocked(n.getKey());
                    if (index < 0) {
                        mNotificationList.add(r);
                        mUsageStats.registerPostedByApp(r);
                    } else {
                        old = mNotificationList.get(index);
                        mNotificationList.set(index, r);
                        mUsageStats.registerUpdatedByApp(r, old);
                        // Make sure we don't lose the foreground service state.
                        notification.flags |=
                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
                        r.isUpdate = true;
                    }

                    mNotificationsByKey.put(n.getKey(), r);

                    // Ensure if this is a foreground service that the proper additional
                    // flags are set.
                    if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
                        notification.flags |= Notification.FLAG_ONGOING_EVENT
                                | Notification.FLAG_NO_CLEAR;
                    }

                    applyZenModeLocked(r);
                    mRankingHelper.sort(mNotificationList);

                    if (notification.icon != 0) {
                        StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
                        mListeners.notifyPostedLocked(n, oldSbn);
                    } else {
                        Slog.e(TAG, "Not posting notification with icon==0: " + notification);
                        if (old != null && !old.isCanceled) {
                            mListeners.notifyRemovedLocked(n);
                        }
                        // ATTENTION: in a future release we will bail out here
                        // so that we do not play sounds, show lights, etc. for invalid
                        // notifications
                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
                                + n.getPackageName());
                    }

                    buzzBeepBlinkLocked(r);
                }
            }
        });

        idOut[0] = id;

我们主要看Handler.post方法中的Runnable方法。
这么最主要的工作就是这个函数:

if (old != null && old.getNotification().isGroupSummary() &&
                            (!notification.isGroupSummary() ||
                                    !old.getGroupKey().equals(r.getGroupKey()))) {
                        cancelGroupChildrenLocked(old, callingUid, callingPid, null);
                    }

这一段的意思就是清掉前一个通知,如果更新而导致数组的大小出现了变化的话。这个发生在过去的通知是个所有的通知的集合,或者当过去的通知是一个集合但是他的key发生了改变。

而函数cancelGroupChildrenLocked中做了一个动作那就是去除尾部的通知同时执行尾部的通知,接下来又调用了cancelNotificationLocked(childR, false, REASON_GROUP_SUMMARY_CANCELED);

 private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
  if (sendDelete) {
            if (r.getNotification().deleteIntent != null) {
                try {
                    r.getNotification().deleteIntent.send();
                } catch (PendingIntent.CanceledException ex) {
                    // do nothing - there's no relevant way to recover, and
                    //     no reason to let this propagate
                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
                }
            }
        }
        ....
   }

我们只需要注重上面那一段通过Notification的deleteIntent(上面在显示Notification的时候已经显示了deleteIntent是PendingIntent)的send方法,执行了PendingIntent。

PendingIntent的send

我们接着看PendingIntent的send的方法,在这里send被重载了好几种方法,最终会到这个send执行:

 public void send(Context context, int code, Intent intent,
            OnFinished onFinished, Handler handler, String requiredPermission)
            throws CanceledException {
        try {
            String resolvedType = intent != null ?
                    intent.resolveTypeIfNeeded(context.getContentResolver())
                    : null;
            int res = mTarget.send(code, intent, resolvedType,
                    onFinished != null
                            ? new FinishedDispatcher(this, onFinished, handler)
                            : null,
                    requiredPermission);
            if (res < 0) {
                throw new CanceledException();
            }
        } catch (RemoteException e) {
            throw new CanceledException(e);
        }
    }

最终会由mTarget.send调用,而mTarget则是IInterSender,这时又会回到PendingIntentRecord。这个时候就可以调用之前存进来的PendingIntent了。
最后由函数sendInner完成:

int sendInner(int code, Intent intent, String resolvedType,
            IIntentReceiver finishedReceiver, String requiredPermission,
            IBinder resultTo, String resultWho, int requestCode,
            int flagsMask, int flagsValues, Bundle options, IActivityContainer container) {
        synchronized(owner) {
            if (!canceled) {
                sent = true;
                if ((key.flags&PendingIntent.FLAG_ONE_SHOT) != 0) {
                    owner.cancelIntentSenderLocked(this, true);
                    canceled = true;
                }
                Intent finalIntent = key.requestIntent != null
                        ? new Intent(key.requestIntent) : new Intent();
                if (intent != null) {
                    int changes = finalIntent.fillIn(intent, key.flags);
                    if ((changes&Intent.FILL_IN_DATA) == 0) {
                        resolvedType = key.requestResolvedType;
                    }
                } else {
                    resolvedType = key.requestResolvedType;
                }
                flagsMask &= ~Intent.IMMUTABLE_FLAGS;
                flagsValues &= flagsMask;
                finalIntent.setFlags((finalIntent.getFlags()&~flagsMask) | flagsValues);

                final long origId = Binder.clearCallingIdentity();

                boolean sendFinish = finishedReceiver != null;
                int userId = key.userId;
                if (userId == UserHandle.USER_CURRENT) {
                    userId = owner.getCurrentUserIdLocked();
                }
                switch (key.type) {
                    case ActivityManager.INTENT_SENDER_ACTIVITY:
                        if (options == null) {
                            options = key.options;
                        } else if (key.options != null) {
                            Bundle opts = new Bundle(key.options);
                            opts.putAll(options);
                            options = opts;
                        }
                        try {
                            if (key.allIntents != null && key.allIntents.length > 1) {
                                Intent[] allIntents = new Intent[key.allIntents.length];
                                String[] allResolvedTypes = new String[key.allIntents.length];
                                System.arraycopy(key.allIntents, 0, allIntents, 0,
                                        key.allIntents.length);
                                if (key.allResolvedTypes != null) {
                                    System.arraycopy(key.allResolvedTypes, 0, allResolvedTypes, 0,
                                            key.allResolvedTypes.length);
                                }
                                allIntents[allIntents.length-1] = finalIntent;
                                allResolvedTypes[allResolvedTypes.length-1] = resolvedType;
                                owner.startActivitiesInPackage(uid, key.packageName, allIntents,
                                        allResolvedTypes, resultTo, options, userId);
                            } else {
                                owner.startActivityInPackage(uid, key.packageName, finalIntent,
                                        resolvedType, resultTo, resultWho, requestCode, 0,
                                        options, userId, container, null);
                            }
                        } catch (RuntimeException e) {
                            Slog.w(ActivityManagerService.TAG,
                                    "Unable to send startActivity intent", e);
                        }
                        break;
                    case ActivityManager.INTENT_SENDER_ACTIVITY_RESULT:
                        key.activity.task.stack.sendActivityResultLocked(-1, key.activity,
                                key.who, key.requestCode, code, finalIntent);
                        break;
                    case ActivityManager.INTENT_SENDER_BROADCAST:
                        try {
                            // If a completion callback has been requested, require
                            // that the broadcast be delivered synchronously
                            owner.broadcastIntentInPackage(key.packageName, uid,
                                    finalIntent, resolvedType,
                                    finishedReceiver, code, null, null,
                                requiredPermission, (finishedReceiver != null), false, userId);
                            sendFinish = false;
                        } catch (RuntimeException e) {
                            Slog.w(ActivityManagerService.TAG,
                                    "Unable to send startActivity intent", e);
                        }
                        break;
                    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;
                }

                if (sendFinish) {
                    try {
                        finishedReceiver.performReceive(new Intent(finalIntent), 0,
                                null, null, false, false, key.userId);
                    } catch (RemoteException e) {
                    }
                }

                Binder.restoreCallingIdentity(origId);

                return 0;
            }
        }
        return ActivityManager.START_CANCELED;
    }

其实写的这么长,我们最终只有这么一句有用:

owner.startActivityInPackage(uid, key.packageName, finalIntent,
                                        resolvedType, resultTo, resultWho, requestCode, 0,
                                        options, userId, container, null);

这里的owner又是指AMS,我们回到AMS去看看,又是调用了什么:

final int startActivityInPackage(int uid, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo,
            String resultWho, int requestCode, int startFlags, Bundle options, int userId,
            IActivityContainer container, TaskRecord inTask) {

        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
                false, ALLOW_FULL_ONLY, "startActivityInPackage", null);

        // TODO: Switch to user app stacks here.
        int ret = mStackSupervisor.startActivityMayWait(null, uid, callingPackage, intent,
                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                null, null, null, options, userId, container, inTask);
        return ret;
    }

看到 mStackSupervisor.startActivityMayWait(null, uid, callingPackage, intent,resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,null, null, null, options, userId, container, inTask);
这个函数,我,恍然大悟,这不就是Activity启动流程中的一部分吗,通过Activity监听器来执行在Activity在任务栈中启动过程。

至此PendingIntent是如何启动四大组件的Activity就分析完毕了。

流程图

PendIntent工作流程

感谢任玉刚大神的android开发探索艺术,以及下面这位大神
PendingIntent的工作原理

### Notification Workflow in Software Development In software development, notifications play an essential role in informing users or systems about specific events or changes within applications. The notification workflow typically involves several stages from event detection to delivery. #### Event Detection The process begins when certain predefined conditions are met inside the system. These could be user actions such as submitting a form, completing a task, or reaching milestones like deadlines being approached. In some cases, these triggers might also come from external sources through APIs or webhooks[^1]. #### Processing Logic Once triggered, processing logic determines what type of message should be sent based on rules defined either statically during development time or dynamically via configuration files. This stage may involve checking permissions, personalizing messages according to recipient preferences (e.g., language), and determining appropriate channels for sending out alerts—email, SMS, push notifications, etc.[^2] #### Queue Management To ensure reliable delivery even under high load scenarios where immediate response isn't always possible due to resource constraints, many implementations use queues. Messages get placed into this buffer temporarily until they can safely reach their destination without causing performance issues elsewhere within the application stack[^3]. ```python import queue def enqueue_notification(notification): q = queue.Queue() q.put(notification) def send_notifications(): while not q.empty(): notify = q.get() # Send each notification here... ``` #### Delivery Mechanism Finally, once processed and dequeued appropriately, actual transmission occurs over chosen mediums directly connecting back-end services with end-users' devices. For instance, emails would go through SMTP servers; mobile pushes require integration with platform-specific providers like Firebase Cloud Messaging (FCM).
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值