博客
关于我
理解PendingIntent
阅读量:290 次
发布时间:2019-03-03

本文共 27234 字,大约阅读时间需要 90 分钟。

一 概述

在 Android 中,我们常常使用 PendingIntent 来表达一种“留待日后处理”的意思。从这个角度来说,PendingIntent 可以被理解为一种特殊的异步处理机制。不过,单就命名而言,PendingIntent 其实具有一定误导性,因为它既不继承于 Intent,也不包含 Intent,它被设计出来的核心理念可以粗略地汇总成四个字——“异步激发”。

很明显,这种异步激发常常是要跨进程执行的。比如说 A 进程作为发起端,它可以从系统“获取”一个 PendingIntent,然后 A 进程可以将 PendingIntent 对象通过 binder 机制“传递”给 B 进程,再由 B 进程在未来某个合适时机,“回调” PendingIntent 对象的 send() 动作,完成激发。

在 Android 系统中,最适合做集中性管理的组件就是 AMS(Activity Manager Service)啦,所以它义不容辞地承担起管理所有 PendingIntent 的职责。这样我们就可以画出如下示意图:

在这里插入图片描述
注意其中的第4步“递送相应的 intent”。这一步递送的 intent 是从何而来的呢?简单地说,当发起端获取 PendingIntent 时,其实是需要同时提供若干 intent 的。这些 intent 和 PendingIntent 只是配套的关系,而不是聚合的关系,它们会被缓存在 AMS 中。日后,一旦处理端将 PendingIntent 的“激发”语义传递到 AMS,AMS 就会尝试找到与这个 PendingIntent 对应的若干 intent,并递送出去。

当然,以上说的只是大概情况,实际的技术细节会更复杂一点儿。下面我们就来谈谈细节。

二 PendingIntent原理

2.1 发起端获取PendingIntent

我们先要理解,所谓的 “发起端获取 PendingIntent” 到底指的是什么。难道只是简单 new 一个 PendingIntent 对象吗?显然不是。此处的 “获取” 动作其实还含有向 AMS “注册” intent 的语义。

在 PendingIntent.java 文件中,我们可以看到有如下几个比较常见的静态函数用来获取 PendingIntent:

  • public static PendingIntent getActivity(Context context, int requestCode, Intent intent, int flags)
  • public static PendingIntent getBroadcast(Context context, int requestCode, Intent intent, int flags)
  • public static PendingIntent getService(Context context, int requestCode, Intent intent, int flags)
  • public static PendingIntent getActivities(Context context, int requestCode, Intent[] intents, int flags)
  • public static PendingIntent getActivities(Context context, int requestCode, Intent[] intents, int flags, Bundle options)

它们就是我们常用的获取 PendingIntent 的动作了。

坦白说,这几个函数的命名可真不怎么样,所以我们简单解释一下。上面的 getActivity() 的意思其实是,获取一个 PendingIntent 对象,而且该对象日后激发时所做的事情是启动一个新 activity。也就是说,当它异步激发时,会执行类似 Context.startActivity() 那样的动作。相应地,getBroadcast() 和 getService() 所获取的 PendingIntent 对象在激发时,会分别执行类似 Context…sendBroadcast() 和 Context.startService() 这样的动作。至于最后两个 getActivities(),用得比较少,激发时可以启动几个 activity。

我们以 getActivity() 的代码来说明问题:

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(context);            IIntentSender target =                ActivityManager.getService().getIntentSender(                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,                    null, null, requestCode, new Intent[] {    intent },                    resolvedType != null ? new String[] {    resolvedType } : null,                    flags, options, context.getUserId());            return target != null ? new PendingIntent(target) : null;        } catch (RemoteException e) {               throw e.rethrowFromSystemServer();        }    }

其中那句 new PendingIntent(target) 创建了 PendingIntent 对象,其重要性自不待言。然而,这个对象的内部核心其实是由上面那个 getIntentSender() 函数得来的。而这个 IIntentSender 核心才是我们真正需要研究的东西。

说穿了,此处的 IIntentSender 对象是个 binder 代理,它对应的 binder 实体是 AMS 中的 PendingIntentRecord 对象。PendingIntent 对象构造之时,IIntentSender 代理作为参数传进来,并记录在 PendingIntent 的 mTarget 域。日后,当 PendingIntent 执行异步激发时,其内部就是靠这个 mTarget 域向 AMS 传递语义的。

我们前文说过,PendingIntent 常常会经由 binder 机制,传递到另一个进程去。而 binder 机制可以保证,目标进程得到的 PendingIntent 的 mTarget 域也是合法的 IIntentSender 代理,而且和发起端的 IIntentSender 代理对应着同一个 PendingIntentRecord 实体。示意图如下:

在这里插入图片描述

2.2 AMS里的PendingIntentRecord

那么 PendingIntentRecord 里又有什么信息呢?它的定义截选如下:

public final class PendingIntentRecord extends IIntentSender.Stub {       ......    final PendingIntentController controller;    final Key key; // 最关键的key域    final int uid;    public final WeakReference
ref; boolean sent = false; boolean canceled = false; ...... String stringName; String lastTagPrefix; String lastTag; final static class Key { final int type; final String packageName; final IBinder activity; final String who; final int requestCode; final Intent requestIntent;// 注意! final String requestResolvedType; final SafeActivityOptions options; Intent[] allIntents;// 注意!记录了当初获取PendingIntent时,用户所指定的所有intent String[] allResolvedTypes; final int flags; final int hashCode; final int userId; private static final int ODD_PRIME_NUMBER = 37; Key(int _t, String _p, IBinder _a, String _w, int _r, Intent[] _i, String[] _it, int _f, SafeActivityOptions _o, int _userId) { type = _t; packageName = _p; activity = _a; who = _w; requestCode = _r; // intent数组中的最后一个 requestIntent = _i != null ? _i[_i.length-1] : null; requestResolvedType = _it != null ? _it[_it.length-1] : null; allIntents = _i;// 所有intent allResolvedTypes = _it; flags = _f; options = _o; userId = _userId; ...... } ...... } ......}

PendingIntentRecord 中有个关键的 key 域。这里的 Key 是 PendingIntentRecord 的内部类,我们看到在 Key 中有关键的数组 allIntents 和 requestIntent。前者记录了当初获取 PendingIntent 时,用户所指定的所有 intent(虽然一般情况下只会指定一个 intent,但类似 getActivities() 这样的函数还是可以指定多个 intent 的),而后者可以粗浅地理解为用户所指定的那个 intent 数组中的最后一个 intent。现在大家应该清楚异步激发时用到的 intent 都存在哪里了吧。

我们再看 Key 的构造函数,可以看到 Key 不光承担着记录信息的作用,它还承担“键值”的作用。

2.3 AMS中的PendingIntentRecord总表

在 AMS 中,管理着系统中所有的 PendingIntentRecord 节点,所以需要把这些节点组织成一张表:

PendingIntentController.java

final HashMap
> mIntentSenderRecords = new HashMap<>();

这张哈希映射表的键值类型就是刚才所说的 PendingIntentRecord.Key。

以后每当我们要获取 PendingIntent 对象时,PendingIntent 里的 mTarget 是这样得到的:AMS 会先查 mIntentSenderRecords 表,如果能找到符合的 PendingIntentRecord 节点,则返回之。如果找不到,就创建一个新的 PendingIntentRecord 节点

因为 PendingIntentRecord 是个 binder 实体,所以经过 binder 机制传递后,客户进程拿到的就是个合法的 binder代理。如此一来,前文的示意图可以进一步修改成下图:

在这里插入图片描述

2.4 AMS里的getIntentSender()函数

现在,我们回过头继续说前文的 getActivity(),以及其调用的 getIntentSender()。我们先列一遍 getActivity() 的原型:

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(context);            IIntentSender target =                ActivityManager.getService().getIntentSender(                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,                    null, null, requestCode, new Intent[] {    intent },                    resolvedType != null ? new String[] {    resolvedType } : null,                    flags, options, context.getUserId());            return target != null ? new PendingIntent(target) : null;        } catch (RemoteException e) {               throw e.rethrowFromSystemServer();        }}
  • context 参数是调用方的上下文
  • requestCode 是个简单的整数,起区分作用
  • intent 是异步激发时将发出的 intent
  • flags 可以包含一些既有的标识,比如 FLAG_ONE_SHOT、FLAG_NO_CREATE、FLAG_CANCEL_CURRENT、FLAG_UPDATE_CURRENT 等等
  • options 可以携带一些额外的数据

getActivity() 的代码很简单,其参数基本上都传给了 getIntentSender()。

ActivityManagerService.java

public IIntentSender getIntentSender(int type,            String packageName, IBinder token, String resultWho,            int requestCode, Intent[] intents, String[] resolvedTypes,            int flags, Bundle bOptions, int userId)
  • type 参数表明 PendingIntent 的类型。getActivity() 和 getActivities() 动作里指定的类型值是 INTENT_SENDER_ACTIVITY,getBroadcast() 和 getService() 和动作里指定的类型值分别是 INTENT_SENDER_BROADCAST 和 INTENT_SENDER_SERVICE。另外,在 Activity.java 文件中,我们还看到一个 createPendingResult() 函数,这个函数表达了发起方的 activity 日后希望得到 result 回馈的意思,所以其内部调用 getIntentSender() 时指定的类型值为 INTENT_SENDER_ACTIVITY_RESULT
  • packageName 参数表示发起端所属的包名
  • token 参数是指代回馈目标方的代理。这是什么意思呢?我们常用的 getActivity()、getBroadcast() 和 getService() 中,只是把这个参数简单地指定为 null,表示这个 PendingIntent 激发时,是不需要发回什么回馈的。不过当我们希望获取类型为INTENT_SENDER_ACTIVITY_RESULT 的 PendingIntent时,就需要指定 token参数了。具体可参考 createPendingResult() 的代码:

Activity.java

public PendingIntent createPendingResult(int requestCode, @NonNull Intent data,            @PendingIntent.Flags int flags) {           String packageName = getPackageName();        try {               data.prepareToLeaveProcess(this);            IIntentSender target =                ActivityManager.getService().getIntentSender(                        ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName,                        mParent == null ? mToken : mParent.mToken,                        mEmbeddedID, requestCode, new Intent[] {    data }, null, flags, null,                        getUserId());            return target != null ? new PendingIntent(target) : null;        } catch (RemoteException e) {               // Empty        }        return null;}

看到了吗?传入的 token 为 Activity 的 mToken 或者其 mParent.mToken。说得简单点儿,AMS 内部可以根据这个 token 找到其对应的 ActivityRecord,日后当 PendingIntent 激发时,AMS 可以根据这个 ActivityRecord 确定出该向哪个目标进程的哪个 Activity 发出 result 语义。

  • resultWho 参数和 token 参数息息相关,一般也是 null 啦。在 createPendingResult() 中,其值为 Activity 的 mEmbeddedID 字符串
  • requestCode 参数是个简单的整数,可以在获取 PendingIntent 时由用户指定,它可以起区分的作用
  • intents 数组参数是异步激发时希望发出的 intent。对于 getActivity()、getBroadcast() 和 getService() 来说,都只会指定一个 intent 而已。只有 getActivities() 会尝试一次传入若干 intent
  • resolvedTypes 参数基本上和 intent 是相关的。一般是这样得到的:
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(                context.getContentResolver()) : null;

这个值常常和 intent 内部的 mData URI 有关系,比如最终的值可能是 URI 对应的 MIME 类型。

  • flags 参数可以指定 PendingIntent 的一些行为特点。它的取值是一些既有的比特标识的组合。目前可用的标识有:FLAG_ONE_SHOT、FLAG_NO_CREATE、FLAG_CANCEL_CURRENT、FLAG_UPDATE_CURRENT 等等。有时候,flags 中还可以附带若干 FILL_IN_XXX 标识。我们把常见的标识定义列举如下:

PendingIntent.java

public static final int FLAG_ONE_SHOT = 1<<30;public static final int FLAG_NO_CREATE = 1<<29;public static final int FLAG_CANCEL_CURRENT = 1<<28;public static final int FLAG_UPDATE_CURRENT = 1<<27;

Intent.java

public static final int FILL_IN_ACTION = 1<<0;public static final int FILL_IN_DATA = 1<<1;public static final int FILL_IN_CATEGORIES = 1<<2;public static final int FILL_IN_COMPONENT = 1<<3;public static final int FILL_IN_PACKAGE = 1<<4;public static final int FILL_IN_SOURCE_BOUNDS = 1<<5;public static final int FILL_IN_SELECTOR = 1<<6;public static final int FILL_IN_CLIP_DATA = 1<<7;

这些以 FILL_IN_ 打头的标志位,主要是在 intent 对象的 fillIn() 函数里起作用:

public int fillIn(Intent other, int flags)

我们以 FILL_IN_ACTION 为例来说明,当我们执行类似 srcIntent.fillIn(otherIntent, …) 的语句时,如果 otherIntent 的 mAction 域不是 null 值,那么 fillIn() 会在以下两种情况下,用 otherIntent 的 mAction 域值为 srcIntent 的 mAction 域赋值:

  1. 当 srcIntent 的 mAction 域值为 null 时
  2. 如果 fillIn 的 flags 参数里携带了 FILL_IN_ACTION 标志位,那么即便 srcIntent 的 mAction 已经有值了,此时也会用 otherIntent 的 mAction 域值强行替换掉 srcIntent 的 mAction 域值

其他 FILL_IN_ 标志位和 FILL_IN_ACTION 的处理方式类似,我们不再赘述。

  • options 参数可以携带一些额外数据。

2.4.1 getIntentSender()函数

ActivityManagerService.java

public IIntentSender getIntentSender(int type,            String packageName, IBinder token, String resultWho,            int requestCode, Intent[] intents, String[] resolvedTypes,            int flags, Bundle bOptions, int userId) {           // NOTE: The service lock isn't held in this method because         // nothing in the method requires the service lock to be held.        enforceNotIsolatedCaller("getIntentSender");        // Refuse possible leaked file descriptors        if (intents != null) {               if (intents.length < 1) {                   ......            }            for (int i=0; i

其中有一段逐条判断 intents[] 的代码,如下:

for (int i=0; i

这段代码说明在获取 PendingIntent 对象时,intent 中是不能携带文件描述符的。而且如果这个 PendingIntent 是那种要发出广播的 PendingIntent,那么 intent 中也不能携带 FLAG_RECEIVER_BOOT_UPGRADE 标识符。“BOOT_UPGRADE” 应该是“启动并升级”的意思,它不能使用 PendingIntent。

getIntentSender() 中最核心的一句应该是 mPendingIntentController.getIntentSender。即调用 PendingIntentController 的 getIntentSender

2.4.2 PendingIntentController.getIntentSender

public PendingIntentRecord getIntentSender(int type, String packageName,     int callingUid, int userId, IBinder token, String resultWho,     int requestCode, Intent[] intents, String[] resolvedTypes,    int flags, Bundle bOptions) {           synchronized (mLock) {               if (DEBUG_MU) Slog.v(TAG_MU, "getIntentSender(): uid=" + callingUid);            ......        //各种 flag 的作用        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);     //关键点:生成 Key,把 intents 传入 key 中        PendingIntentRecord.Key key =             new PendingIntentRecord.Key(type, packageName, token,                    resultWho, requestCode, intents, resolvedTypes, flags,                    SafeActivityOptions.fromBundle(bOptions), userId);            WeakReference
ref; ref = mIntentSenderRecords.get(key); PendingIntentRecord rec = ref != null ? ref.get() : null; // 找到了匹配的 PendingIntentRecord,现在考虑要不要更新它,或者取消它 if (rec != null) { if (!cancelCurrent) { //不取消 if (updateCurrent) { // 如果明确指定了 FLAG_UPDATE_CURRENT,那么更新找到的节点 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 这个节点 return rec; } //如果PendingIntent的标志中带有FLAG_CANCEL_CURRENT,则从哈希映射表中删除之 makeIntentSenderCanceled(rec); mIntentSenderRecords.remove(key); } if (noCreate) { // 如果明确表示了不创建新节点,也就是说标志中带有FLAG_NO_CREATE, // 那么不管是不是Cancel了PendingIntent,此时一概直接返回 return rec; } // 从哈希映射表中找不到,而且又没有写明FLAG_NO_CREATE,此时创建一个新节点 //创建一个新的PendingIntentRecord添加到mIntentSenderRecords中 rec = new PendingIntentRecord(this, key, callingUid); mIntentSenderRecords.put(key, rec.ref); return rec; }}

上面这段代码主要做的事情有:

  • 将传进来的多个参数信息整理成一个 PendingIntentRecord.Key 对象
  • 尝试从 mIntentSenderRecords 总表中查找和 key 相符的 PendingIntentRecord 节点
  • 根据 flags 参数所含有的意义,对得到的 PendingIntentRecord 进行加工。有时候修改之,有时候删除之
  • 如果在总表中没有找到对应的 PendingIntentRecord 节点,或者根据 flags 的语义删除了刚找到的节点,那么此时的默认行为是创建一个新的 PendingIntentRecord 节点,并插入总表。除非 flags 中明确指定了FLAG_NO_CREATE,此时不会创建新节点

2.4.3 说说flags

通过以上代码,我们应该明白了 flags 中那些特定比特值的意义了。我们现在总结一下。

应该说这些 flags 比特值基本上都是在围绕着 mIntentSenderRecords 总表说事的。

  • FLAG_CANCEL_CURRENT 的意思是,当我们获取 PendingIntent 时,如果可以从总表中查到一个相符的已存在的 PendingIntentRecord 节点的话,那么需要把这个节点从总表中清理出去
  • 而在没有指定 FLAG_CANCEL_CURRENT 的大前提下,如果用户指定了 FLAG_UPDATE_CURRENT 标识,那么会用新的 intents 参数替掉刚查到的 PendingIntentRecord 中的旧 intents
  • 而不管是刚清理了已存在的 PendingIntentRecord,还是压根儿就没有找到符合的 PendingIntentRecord,只要用户没有明确指定 FLAG_NO_CREATE 标识,系统就会尽力创建一个新的 PendingIntentRecord 节点,并插入总表
  • 至于 FLAG_ONE_SHOT 标识,它并没有在 getIntentSender() 中露脸儿。它的名字是 “FLAG_ONE_SHOT”,也就是“只打一枪”的意思,那么很明显,这个标识起作用的地方应该是在“激发”函数里。在最终的激发函数(sendInner())里,我们可以看到下面的代码:

frameworks/base/services/java/com/android/server/am/PendingIntentRecord.java

public int sendInner(int code, Intent intent, String resolvedType,    IBinder whitelistToken, IIntentReceiver finishedReceiver,    String requiredPermission, IBinder resultTo, String resultWho,    int requestCode, int flagsMask, int flagsValues, Bundle options) {           if (intent != null) intent.setDefusable(true);        if (options != null) options.setDefusable(true);       ......        synchronized (controller.mLock) {               if (canceled) {                   return ActivityManager.START_CANCELED;            }            sent = true;            if ((key.flags & PendingIntent.FLAG_ONE_SHOT) != 0) {               //关键点:调用 PendingIntentController 的 cancelIntentSender                controller.cancelIntentSender(this, true);            }            ......        }        ......    }

PendingIntentController.java

public void cancelIntentSender(IIntentSender sender) {           if (!(sender instanceof PendingIntentRecord)) {               return;        }        synchronized (mLock) {               final PendingIntentRecord rec = (PendingIntentRecord) sender;            try {                   ......                }            } catch (RemoteException e) {                   throw new SecurityException(e);            }            cancelIntentSender(rec, true);        }    }    public void cancelIntentSender(PendingIntentRecord rec, boolean cleanActivity) {           synchronized (mLock) {           //设置 canceled 域设为 true            makeIntentSenderCanceled(rec);            mIntentSenderRecords.remove(rec.key);            if (cleanActivity && rec.key.activity != null) {                   final Message m = PooledLambda.obtainMessage(                        PendingIntentController::clearPendingResultForActivity, this,                        rec.key.activity, rec.ref);                mH.sendMessage(m);            }        }    }private void makeIntentSenderCanceled(PendingIntentRecord rec) {           rec.canceled = true; //设置 canceled 域设为 true        final RemoteCallbackList
callbacks = rec.detachCancelListenersLocked(); if (callbacks != null) { final Message m = PooledLambda.obtainMessage( PendingIntentController::handlePendingIntentCancelled, this, callbacks); mH.sendMessage(m); } final AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class); ami.remove(new PendingIntent(rec)); }

意思很简单,一进行激发就把相应的 PendingIntentRecord 节点从总表中清理出去,而且把 PendingIntentRecord 的 canceled 域设为 true。这样,以后即便外界再调用 send() 动作都没用了,因为再也无法进入 if (!canceled) 判断了。

2.5 PendingIntent的激发动作

下面我们来看 PendingIntent 的激发动作。在前文我们已经说过,当需要激发 PendingIntent 之时,主要是通过调用 PendingIntent 的 send() 函数来完成激发动作的。PendingIntent 提供了多个形式的 send() 函数,然而这些函数的内部其实调用的是同一个 send(),其函数如下:

public void send(Context context, int code, @Nullable Intent intent,            @Nullable OnFinished onFinished, @Nullable Handler handler,            @Nullable String requiredPermission, @Nullable Bundle options)            throws CanceledException {               //关键点:调用 sendAndReturnResult        if (sendAndReturnResult(context, code, intent, onFinished, handler,        requiredPermission,                options) < 0) {               throw new CanceledException();        }    }public int sendAndReturnResult(Context context, int code, @Nullable Intent intent,            @Nullable OnFinished onFinished, @Nullable Handler handler,            @Nullable String requiredPermission, @Nullable Bundle options)            throws CanceledException {           try {               String resolvedType = intent != null ?                    intent.resolveTypeIfNeeded(context.getContentResolver())                    : null;            //关键点:调用AMS的sendIntentSender            //注意其中的 mTarget 参数 就是IIntentSender代理            //对应着AMS中的一个Binder实体:PendingIntentRecord            return ActivityManager.getService().sendIntentSender(                    mTarget, mWhitelistToken, code, intent, resolvedType,                    onFinished != null                            ? new FinishedDispatcher(this, onFinished, handler)                            : null,                    requiredPermission, options);        } catch (RemoteException e) {               throw new CanceledException(e);        }    }

ActivityManagerService.java

public int sendIntentSender(IIntentSender target, IBinder whitelistToken, int code,            Intent intent, String resolvedType,            IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {           if (target instanceof PendingIntentRecord) {   //走到此分支            return ((PendingIntentRecord)target).sendWithResult(code, intent, resolvedType,                    whitelistToken, finishedReceiver, requiredPermission, options);        } else {               if (intent == null) {                   ......                Slog.wtf(TAG, "Can't use null intent with direct IIntentSender call");                intent = new Intent(Intent.ACTION_MAIN);            }            try {                   target.send(code, intent, resolvedType, whitelistToken, null,                        requiredPermission, options);            } catch (RemoteException e) {               }            ......            if (finishedReceiver != null) {                   try {                       finishedReceiver.performReceive(intent, 0,                            null, null, false, false, UserHandle.getCallingUserId());                } catch (RemoteException e) {                   }            }            return 0;        }    }

PendingIntentRecord.java

public int sendWithResult(int code, Intent intent, String resolvedType,    IBinder whitelistToken, IIntentReceiver finishedReceiver,    String requiredPermission, Bundle options) {           return sendInner(code, intent, resolvedType, whitelistToken, finishedReceiver,                requiredPermission, null, null, 0, 0, 0, options);    }    public int sendInner(int code, Intent intent, String resolvedType,        IBinder whitelistToken, IIntentReceiver finishedReceiver,        String requiredPermission, IBinder resultTo, String resultWho,        int requestCode, int flagsMask, int flagsValues, Bundle options) {           if (intent != null) intent.setDefusable(true);        if (options != null) options.setDefusable(true);        ......        synchronized (controller.mLock) {               if (canceled) {                   return ActivityManager.START_CANCELED;            }            sent = true;            if ((key.flags & PendingIntent.FLAG_ONE_SHOT) != 0) {                   controller.cancelIntentSender(this, true);            }            finalIntent = key.requestIntent != null ?            new Intent(key.requestIntent) : new Intent();            ......            final ActivityOptions opts = ActivityOptions.fromBundle(options);            if (opts != null) {                   finalIntent.addFlags(opts.getPendingIntentLaunchFlags());            }            ......            if (key.type == ActivityManager.INTENT_SENDER_ACTIVITY                    && key.allIntents != null && key.allIntents.length > 1) {                 allIntents = new Intent[key.allIntents.length];             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;            }        }      ......        int res = START_SUCCESS;        try {               ......            boolean sendFinish = finishedReceiver != null;            int userId = key.userId;            if (userId == UserHandle.USER_CURRENT) {                   userId = controller.mUserController.getCurrentOrTargetUserId();            }            final boolean allowTrampoline = uid != callingUid                    && controller.mAtmInternal.isUidForeground(callingUid);            switch (key.type) {                   case ActivityManager.INTENT_SENDER_ACTIVITY:                    try {                           if (key.allIntents != null && key.allIntents.length > 1) {                           //关键点,调用ATM执行activity的启动工作                       res = controller.mAtmInternal.startActivitiesInPackage(                          uid, callingPid, callingUid, key.packageName, allIntents,                          allResolvedTypes, resultTo, mergedOptions, userId,                          false /* validateIncomingUser */,                          this /* originatingPendingIntent */,mAllowBgActivityStartsForActivitySender.contains(whitelistToken));                        } else {                         ......                        }                    } catch (RuntimeException e) {                           Slog.w(TAG, "Unable to send startActivity intent", e);                    }                    break;                ......            }            if (sendFinish && res != ActivityManager.START_CANCELED) {                   try {                       finishedReceiver.performReceive(new Intent(finalIntent), 0,                            null, null, false, false, key.userId);                } catch (RemoteException e) {                   }            }        } finally {               Binder.restoreCallingIdentity(origId);        }        return res;    }

sendInner() 完成的主要逻辑动作有:

  • 如果当前 PendingIntentRecord 节点已经处于 canceled 域 为true 的状态,那么说明这个节点已经被取消掉了,此时 sendInner() 不会做任何实质上的激发动作,只是简单地 return ActivityManager.START_CANCELED 而已
  • 如果当初在创建这个节点时,使用者已经指定了 FLAG_ONE_SHOT 标志位的话,那么此时 sendInner() 会把这个 PendingIntentRecord 节点从 AMS 中的总表中摘除,并且把 canceled 域设为 true。而后的操作和普通激发时的动作是一致的,也就是说也会走下步
  • 关于普通激发时应执行的逻辑动作是,根据当初创建 PendingIntentRecord 节点时,用户指定的 type 类型,进行不同的处理。这个 type 其实就是我们前文所说的 INTENT_SENDER_ACTIVITY、INTENT_SENDER_BROADCAST、INTENT_SENDER_SERVICE 等类型啦,大家如有兴趣,可自己参考本文一开始所说的getActivity()、getBroadcast()、getService() 等函数的实现代码

现在还有一个问题是,既然我们在当初获取 PendingIntent 时,已经指定了日后激发时需要递送的 intent(或 intent 数组),那么为什么 send() 动作里还有一个 intent 参数呢?它们的关系又是什么呢?我猜想,PendingIntent 机制的设计者是希望给激发端一个修改 “待激发的 intent” 的机会。比如当初我们获取 PendingIntent 对象时,如果在 flags 里设置了 FILL_IN_ACTION 标志位,那么就说明我们允许日后在某个激发点,用新的 intent 的 mAction 域值,替换掉我们最初给的 intent 的 mAction 域值。如果一开始没有设置 FILL_IN_ACTION 标志位,而且在最初的 intent 里已经有了非空的 mAction 域值的话,那么即使在激发端又传入了新 intent,它也不可能修改用新 intent 的 mAction 域值替换旧 intent 的 mAction 域值。

细心的读者一定记得,当初获取 PendingIntent 对象时,我们可是向 AMS 端传递了一个 intent 数组噢,虽然一般情况下这个数组里只有一个 intent 元素,但有时候我们也是有可能一次性传递多个 intent 的。比如 getActivities() 函数就可以一次传递多个 intent。可是现在激发动作 send() 却只能传递一个 intent 参数,这该如何处理呢?答案很简单,所传入的 intent 只能影响已有的 intent 数组的最后一个 intent 元素。大家可以看看 sendInner 里 allIntents[allIntents.length-1] = finalIntent; 一句。

三 小结及使用场景

大体的原理是: A应用希望让B应用帮忙触发一个行为,这是跨应用的通信,需要 Android 系统作为中间人,这里的中间人就是 ActivityManager。 A应用创建建 PendingIntent,在创建 PendingIntent 的过程中,向 ActivityManager 注册了这个 PendingIntent,所以,即使A应用死了,当它再次苏醒时,只要提供相同的参数,还是可以获取到之前那个 PendingIntent 的。当 A 将 PendingIntent 调用系统 API 比如 AlarmManager.set(),实际是将权限给了B应用,这时候, B应用可以根据参数信息,来从 ActivityManager 获取到 A 设置的 PendingIntent。

已知的使用场景是:

  • 通知,在点击通知时执行调起本应用的操作,当然也可以执行其他操作
  • 闹钟,定时执行某个操作
  • 桌面小部件,点击小部件时执行某个操作

通知,闹钟,桌面小部件,都是运行在其他应用中的,但是给我们的感知就像是我们自己的应用的一部分。

转载地址:http://fqom.baihongyu.com/

你可能感兴趣的文章
CSS浮动属性
查看>>
SVM多类识别
查看>>
svn 撤销已提交的错误修改
查看>>
算法工程师数学理论提高札记(improving)
查看>>
仿微信--主要版本说明
查看>>
Android存储
查看>>
Android网络优化--精准获取流量消耗
查看>>
Android进程的启动流程
查看>>
异步任务--AsyncTask
查看>>
《硬件架构的艺术》学习笔记(3.1)---跨时钟域设计
查看>>
Filecoin官方发布:并不存在“双花”问题!
查看>>
VTK:图表之ShortestPath
查看>>
VTK:IO之DumpXMLFile
查看>>
VTK:IO之JPEGReader
查看>>
VTK:IO之MetaImageReader
查看>>
VTK:IO之WriteVTI
查看>>
VTK:图片之Actor2D
查看>>
VTK:图片之ImageCorrelation
查看>>
VTK:图片之ImageExport
查看>>
VTK:图片之ImageMathematics
查看>>