Android PendingIntent 的一些小迷惑

近日在开发中刚好涉及到桌面 Widget 的一些开发工作,而桌面 Widget 控件的点击事件,通常只能通过 RemoteViews.setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) 方法来指定响应的行为。

通常实际应用中我们会把桌面 Widget 作为应用的快捷方式和缩略展示,那么通常我们做的事情一般是点击桌面 Widget 上某控件后,跳转到对应的 Activity 中,那么我们就需要使用到 PendingIntent.getActivity(Context context, int requestCode, Intent intent, int flags) 来获取一个 PendingIntent 实例,通常我们会在 Intent 中指定我们的目标 Activity,并通过 putExtra 方法来传递一些必要的参数。例如:

Intent intent = new Intent(context, MainActivity.class);

intent.putExtra(“GREETING”,”HelloWorld”);

PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,intent, 0);

remoteViews.setOnClickPendingIntent(R.id.widget_goto_main, pendingIntent);

上面的这个代码呢,主要目的就是能让桌面 Widget 上的控件响应点击事件,并且能直接进入 MainActivity。这里我们将 requestCode 和 flags 都设置为 0 了,目前 Android 中还没有使用到 requestCode 来做什么控制,只是预留了这么一个参数方便于未来的扩展,但是 Flag 能就非常有用了,因为系统会通过 Flag 来识别需要进行的行为。我么先来看一下官方文档上的一段话:

A PendingIntent itself is simply a reference to a token maintained by the system describing the original data used to retrieve it. This means that, even if its owning application’s process is killed, the PendingIntent itself will remain usable from other processes that have been given it. If the creating application later re-retrieves the same kind of PendingIntent (same operation, same Intent action, data, categories, and components, and same flags), it will receive a PendingIntent representing the same token if that is still valid, and can thus call cancel() to remove it.

我翻译一下:一个 PendingIntent 就是一个 Android 系统管理并持有的用于描述和获取原始数据的对象的标志 (引用)。这就意味着,即便创建该 PendingIntent 对象的进程被杀死了,这个 PendingItent 对象自己在其他进程中还是可用的。如果创建该 PendingIntent 对象的进程随后又重新获取了一个同类型的 PendingIntent(对于程序来讲,就是通过同样的方法获取的,例如都是通过 getActivity、getBroadcast、getService 方法来获取的,并且传递给 getXXX 方法的 Intent 对象的 Action 是相同的,Data 也是相同的,Categories 也是相同的,Components 也是相同的,Flags 也是相同的),如果之前获取的 PendingIntent 对象还有效的话,那么该进程获取到的 PendingItent 对象将获得同一个对象的引用,而且可以通过 cancel() 方法来从系统中移除它。

如果我们只是想通过设置不同的 Extra 来生成不同的 PendingIntent 对象是行不通的,因为 PendingIntent 对象由系统持有,并且系统只通过刚才在上面提到的几个要素来判断 PendingIntent 对象是否是相同的,那么如果我们想在每次更新 Widget 的时候也更新 PendingIntent 对象的话,我们应该怎么做的,目前我能想到的就是通过设置 Flag 的方式来做。

目前在 Android 中有以下 flag:

FLAG_CANCEL_CURRENT: 如果当前系统中已经存在一个相同的 PendingIntent 对象,那么就将先将已有的 PendingIntent 取消,然后重新生成一个 PendingIntent 对象。

FLAG_NO_CREATE: 如果当前系统中不存在相同的 PendingIntent 对象,系统将不会创建该 PendingIntent 对象而是直接返回 null。

FLAG_ONE_SHOT: 该 PendingIntent 只作用一次,如果该 PendingIntent 对象已经触发过一次,那么下次再获取该 PendingIntent 并且再触发时,系统将会返回一个 SendIntentException,在使用这个标志的时候一定要注意哦。

FLAG_UPDATE_CURRENT: 如果系统中已存在该 PendingIntent 对象,那么系统将保留该 PendingIntent 对象,但是会使用新的 Intent 来更新之前 PendingIntent 中的 Intent 对象数据,例如更新 Intent 中的 Extras。这个非常有用,例如之前提到的,我们需要在每次更新之后更新 Intent 中的 Extras 数据,达到在不同时机传递给 MainActivity 不同的参数,实现不同的效果。

大概就是这么多了,:-),其实自己也还是有点晕,以后有工夫了再慢慢补充吧,先做个记录也好!整个四月份都没有发过博客了,在四月份和五月份交接的此时此刻,发布一篇,以做纪念吧,:-)。

  • 创意人

    看到你在 showeb20 上的评论,很不错的说~ 博客内容也不错,我会经常来学习学习移动互联网相关的技术。

    • 太客气了,:-),相互学习。
      其实那个评论有一点点广告的嫌疑哦,:-),不知道你发现没有哦!

  • tingting

    Lishali: 你好,我是触景无限的,很想和你见面聊聊。
    婷婷
    18601077618

  • lishali

    你好,见面还是算了吧,嘿嘿,我就是个小球

  • Pingback: 发通知 PendingIntent 中Intent 内容没有更新_How Android » How Android()

  • mapler

    在做一个闹钟程序时也碰到了 PendingIntent 里 Extra 内容不更新的问题。折腾了 2 个多小时,最后啃官方文档才发现是 Flag 的问题,虽然问题解决了但还是比较晕,于是搜了一下,原来你这里有讨论了。
    看了你写的和 Trackbck 里的讨论,但终于思路理清楚了
    非常感谢啊!

    • 能解决问题就是最好滴

  • Pingback: PendingIntent的深入理解 | 互联网纪事()

  • 一明惊人

    感谢 shali

  • lili

    如果 PendingIntent 的进程也被杀死了怎么办

    • 你指的 PendingIntent 的进程也被杀死了时什么意思?使用 PendingIntent 的进程被杀死了还是 PendingIntent 指定的 Activity、Service 或者 BroadcastReceiver 所属进程被杀死?

      如果使用 PendingIntent 的进程被杀死,确实可能会影响到 PendingIntent 的执行,不过还是要看你具体如何使用的,以及你碰到的实际问题是什么

  • yexxx

    请教一个,我目前正在开发一个类似闹钟的小程序,如何设定很多个的闹钟,而又互不影响呢?
    如果 PendingIntent 的 flag 用的 FLAG_UPDATE_CURRENT,那一个新的闹钟任务不是会把旧的待响的闹钟任务更新掉吗?

    • 如果你的行为在 PendingIntent 中定义的是一致的,例如 Action 是相同的那么肯定会被更新的,不过通常闹钟的做法应该不是用户设置了 5 个闹钟然后就一次性的将 5 个闹钟的 PendingIntent 构建出来,交给 AlarmManager。而是需要将用户设置的五个闹钟信息按一定顺序存储起来,当用户的第一个闹钟被触发时,再去查找第二个闹钟信息,然后将第二个闹钟信息设置给 AlarmManager

  • yexxx

    原来是这样。几天来的问题得以解决,十分感谢博主!

    • 能解决你的问题,能帮到你是最好的,哈哈

  • Pingback: AndroidPendingIntent的一些小迷惑 - 移动开发教程 - 开发者()

  • Pingback: 发通知PendingIntent中Intent内容没有更新 - 移动开发教程 - 开发者()