android PendingIntent理解

PendingIntent是Android框架的重要组成部分。Android 12创建的每个PendingIntent对象必须使用PendingIntent.FLAG_MUTABLE或PendingIntent.FLAG_IMMUTABLE标志指定可变性,以提高应用的安全性。

PendingIntent对象包装了Intent对象的功能,同时允许指定另一个应用程序代替自己执行后续的操作。
PendingIntent的主要功能是其包装的Intent被另一个应用程序代替调用执行。也就是说,另一个应用程序以发送此Intent的应用程序的身份执行此Intent。
为了使PendingIntent具有与普通Intent相同的行为,系统根据创建时使用的相同标识来触发PendingIntent。

使用PendingIntent的最常见、最基本的方法是与通知关联操作:

Intent intent = Intent(applicationContext, MainActivity.class);
intent.setAction(NOTIFICATION_ACTION);
intent.setData(deepLink);
PendingIntent pendingIntent = PendingIntent.getActivity(applicationContext, NOTIFICATION_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE);
Notification notification = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL).setContentIntent(pendingIntent).build();
notificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, notification);

上面代码构建一个标准的Intent来打开应用程序,然后在将它添加到通知之前简单地将它包装在PendingIntent中。
在这种情况下,由于动作是确定的不可变的,因此使用FLAG_IMMUTABLE标志构造一个 PendingIntent,也就是说接收此PendingIntent的应用程序不能对其进行修改。
在调用NotificationManagerCompat.notify()之后,系统将显示通知,并且当用户点击该通知时,会调用PendingIntent.send()方法启动应用程序。

更新不可变的PendingIntent

并不是应用程序需要更新PendingIntent那么它就需要是可变的。创建PendingIntent的应用程序始终可以通过传递标志FLAG_UPDATE_CURRENT来更新它:

Intent updatedIntent = Intent(applicationContext, MainActivity.class);
updatedIntent.setAction(NOTIFICATION_ACTION);
updatedIntent.setData(differentDeepLink);
PendingIntent updatedPendingIntent = PendingIntent.getActivity(applicationContext, NOTIFICATION_REQUEST_CODE, updatedIntent, PendingIntent.FLAG_IMMUTABLE|PendingIntent.FLAG_UPDATE_CURRENT);

应用间API

PendingIntent不仅仅可以用来与系统进行交互,其他地方也可以用。使用startActivityForResult()启动一个活动并且在onActivityResult()中接收回调并不是唯一的方法。
假设有一个在线订购应用程序,它提供了API允许其他应用程序与其通信。它可能会接受PendingIntent作为它自己的Intent的extra参数,用于开始订购食物的过程。订单应用程序只有在订单交付后才会启动PendingIntent。
在这种情况下,订购应用程序使用PendingIntent而不是发送activity结果,因为交付订单可能需要大量时间,并且在这种情况发生时强制让用户等待是没有意义的。
因为不希望在线订购应用程序更改Intent的任何内容,只希望在订单到达时按原样发送,因此需要创建一个不可变的PendingIntent对象。

可变PendingIntent

但是,如果订购应用程序的开发人员想要添加一项功能,允许用户输入一条消息,该消息将被发送回调用它的应用程序,这时候就要使用可变的PendingIntent。
由于PendingIntent本质上是Intent的包装,所以你可能会认为有一个PendingIntent.getIntent()的方法,可以调用它来获取和更新包装的Intent,但事实并非如此。那么它是如何工作的呢?
除了PendingIntent上的send()方法不带任何参数外,还有一些其他版本:

public void send(Context context, int code, @Nullable Intent intent)

这个传入的Intent参数不会替换PendingIntent中包含的Intent,而是用于填充在创建PendingIntent时未提供的包装Intent中的参数。

Intent orderDeliveredIntent = Intent(applicationContext, OrderDeliveredActivity.class);
orderDeliveredIntent.setAction(ACTION_ORDER_DELIVERED);
PendingIntent mutablePendingIntent = PendingIntent.getActivity(applicationContext, NOTIFICATION_REQUEST_CODE, orderDeliveredIntent, PendingIntent.FLAG_MUTABLE);

这个PendingIntent可以移交给在线订购应用程序。交付完成后,订单应用程序可以接收customerMessage并将其作为Intent extra发送回来,如下所示:

Intent intentWithExtrasToFill = new Intent();
intentWithExtrasToFill.putExtra(EXTRA_CUSTOMER_MESSAGE, customerMessage);
mutablePendingIntent.send(applicationContext, PENDING_INTENT_CODE, intentWithExtrasToFill);

然后,调用应用程序将在其Intent中看到额外的EXTRA_CUSTOMER_MESSAGE并能够显示消息。

声明PendingIntent可变性时的重要注意事项

创建可变PendingIntent时,始终明确设置将在Intent中启动的组件。这可以通过上面完成的方式完成,通过显式设置将接收它的组件类,但也可以通过调用Intent.setComponent()来完成。

应用程序调用Intent.setPackage()似乎更容易。如果这样做必须小心匹配多个组件的可能性。如果可能的话,最好指定特定的组件来接收Intent。

如果尝试覆盖使用FLAG_IMMUTABLE创建的PendingIntent中的值,则会失败,仍然会传递未修改的原始包装Intent对象。

应用程序始终可以更新自己的PendingIntent对象,即使它们是不可变的。使PendingIntent可变的唯一原因是另一个应用程序必须能够以某种方式更新包装的Intent对象。

flags细节

已经讨论了一些在创建PendingIntent时可以使用的标志,但还有一些其他的标志。

  • FLAG_IMMUTABLE:表示PendingIntent中的Intent不能被将Intent传递给PendingIntent.send()的其他应用程序修改。应用总是可以使用FLAG_UPDATE_CURRENT来修改它自己的PendingIntent
    在Android 12之前,默认情况下,不带此标志创建的PendingIntent是可变的。
    在Android 6 (API 23) 之前的Android版本上,PendingIntent总是可变的。

  • FLAG_MUTABLE:表示PendingIntent中的Intent应允许应用程序通过合并PendingIntent.send()的Intent参数值来更新其内容。
    始终填写任何可变的PendingIntent的包装Intent的ComponentName。不这样做可能会导致安全漏洞!
    此标志是在Android 12中添加的。在Android 12之前,在没有FLAG_IMMUTABLE标志的情况下创建的任何PendingIntent都是隐式可变的。

  • FLAG_UPDATE_CURRENT:请求系统用新的extra数据更新现有的PendingIntent,而不是存储新的PendingIntent。如果PendingIntent没有注册,那么将会被注册。

  • FLAG_ONE_SHOT:只允许发送一次PendingIntent(通过PendingIntent.send())。如果在将PendingIntent传递给另一个应用程序并且其中的Intent只能发送一次时此标志很重要。这可能是为了方便或者是为了防止应用程序多次执行某些操作。
    使用FLAG_ONE_SHOT可以防止诸如“重放攻击”(replay attacks)之类的问题。

  • FLAG_CANCEL_CURRENT:取消现有的PendingIntent(如果存在),然后再注册新的PendingIntent。如果某个特定的PendingIntent已经被发送到了一个应用程序,与此同时又想将它发送到另一个应用程序,而且还有可能会更新数据,则此标志很重要。通过使用FLAG_CANCEL_CURRENT,第一个应用程序将无法再调用其send方法,但第二个应用程序可以。

接收PendingIntent

有时系统或其他框架会提供PendingIntent作为API调用的返回。例如在Android 11中添加的MediaStore.createWriteRequest()方法。

public static PendingIntent MediaStore.createWriteRequest(@NonNull ContentResolver resolver, @NonNull Collection<Uri> uris)

正如应用程序创建的PendingIntent使用应用程序身份运行一样,系统创建的PendingIntent也是使用系统身份运行。就此API而言,其允许应用程序启动一个 Activity,该Activity可以向应用程序授予对Uris集合的写入权限。

案例

 public static void startActivityForService(Context context, MediaInfo mediaInfo) {
    	Intent intent = new Intent(context, ScreenshotResultActivity.class);
        intent.putExtra(EXTRA_MEDIA_INFO, mediaInfo);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
        try {
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
            pendingIntent.send();
        } catch (Exception ex) {
            ex.printStackTrace();
            context.startActivity(intent);
        }
}

感谢大家的支持,如有错误请指正,如需转载请标明原文出处!

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值