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不同的参数,实现不同的效果。

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

Android PendingIntent的一些小迷惑》有17个想法

  1. 创意人

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

    回复
  2. Pingback引用通告: 发通知 PendingIntent 中Intent 内容没有更新_How Android » How Android

  3. mapler

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

    回复
  4. Pingback引用通告: PendingIntent的深入理解 | 互联网纪事

    1. lishali 文章作者

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

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

      回复
  5. yexxx

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

    回复
    1. lishali 文章作者

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

      回复
  6. Pingback引用通告: AndroidPendingIntent的一些小迷惑 - 移动开发教程 - 开发者

  7. Pingback引用通告: 发通知PendingIntent中Intent内容没有更新 - 移动开发教程 - 开发者

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据