月度归档:2011年04月

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

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