1
头图

PendingIntent is a very important part of the Android framework, but most of the developer resources related to this topic pay more attention to its implementation details, that is, "PendingIntent is a token reference maintained by the system", and ignores its purpose. .

Because Android 12 Dui PendingIntent were important updates , including the need to explicitly determine whether PendingIntent is variable, and so I think we need to talk in depth PendingIntent what role, how the system uses it and why you need to be PendingIntent of variable type.

What is PendingIntent?

The PendingIntent object encapsulates the functionality of the Intent object, and at the same time specifies what operations are allowed by other applications in your application's name to respond to future operations the user will perform. For example, the encapsulated Intent may be triggered after the alarm is turned off or when the user clicks on the notification.

The key point is PendingIntent other applications when triggered intent is in the name of your application . In other words, other apps will use your app's identity to trigger intents.

In order to make the PendingIntent have the same function as the ordinary Intent, the system will use the identity when the PendingIntent was created to trigger it. In most cases, such as alarms and notifications, the identity used is the application itself.

Let's look at the different ways in which PendingIntent is used in the application, and why we use these ways.

Regular usage

The PendingIntent is as an operation associated with a notification.

val intent = Intent(applicationContext, MainActivity::class.java).apply {
    action = NOTIFICATION_ACTION
    data = deepLink
}
val pendingIntent = PendingIntent.getActivity(
    applicationContext,
    NOTIFICATION_REQUEST_CODE,
    intent,
    PendingIntent.FLAG_IMMUTABLE
)
val notification = NotificationCompat.Builder(
        applicationContext,
        NOTIFICATION_CHANNEL
    ).apply {
        // ...
        setContentIntent(pendingIntent)
        // ...
    }.build()
notificationManager.notify(
    NOTIFICATION_TAG,
    NOTIFICATION_ID,
    notification
)

You can see that we built a standard type of Intent to open our application, and then simply encapsulated it PendingIntent

In this case, because we clearly know what the future needs of the operation, so we use FLAG_IMMUTABLE marker construct that can not be modified PendingIntent .

After calling NotificationManagerCompat.notify() ), the work is complete. When a notification system, and the user clicks on the notification, will be in our PendingIntent call on PendingIntent.send () , to start our application.

Update immutable PendingIntent

You might think that if the application needs to update PendingIntent , then it needs to be a variable type, but it is not. PendingIntent created by the application can be updated with the FLAG_UPDATE_CURRENT

val updatedIntent = Intent(applicationContext, MainActivity::class.java).apply {
   action = NOTIFICATION_ACTION
   data = differentDeepLink
}

// 由于我们使用了 FLAG_UPDATE_CURRENT 标记,所以这里可以更新我们在上面创建的 
// PendingIntent
val updatedPendingIntent = PendingIntent.getActivity(
   applicationContext,
   NOTIFICATION_REQUEST_CODE,
   updatedIntent,
   PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
// 该 PendingIntent 已被更新

In the following content we will explain why PendingIntent set as a variable type.

Cross-application API

The usual usage is not limited to interacting with the system. Although after some operations startActivityForResult () ) and the onActivityResult () ) to receive callbacks is very common usage, but it is not the only usage.

Imagine that an online ordering application provides an API to enable other applications to integrate. When Intent After starting the ordering of food processes, applications can Intent of extra way to access PendingIntent . Once the order is delivered, the ordering application only needs to start PendingIntent once.

In this example, the ordering application uses PendingIntent without sending the activity result directly, because the order may take longer to submit, and it is unreasonable for the user to wait in the process.

We want to create an immutable PendingIntent because we don’t want the online ordering application to modify our Intent . When the order is valid, we only want other apps to send it and keep it unchanged.

Variable PendingIntent

But what if we, as a developer of an ordering application, want to add a feature that allows users to send messages back to the application that calls the ordering function? For example, the calling application can prompt, "It's pizza time!"

To achieve this effect, you need to use a variable PendingIntent.

Since PendingIntent is essentially Intent package, one might think by a PendingIntent.getIntent() be obtained in which the encapsulated method Intent . But the answer is no. So how can it be achieved?

PendingIntent In addition no parameters send() method, there are other versions of the send method, comprising the acceptable Intent as a parameter version ):

fun PendingIntent.send(
   context: Context!,
   code: Int,
   intent: Intent?
)

Here Intent parameters not replaced PendingIntent encapsulated Intent , but by PendingIntent when creating the encapsulated Intent to filling parameter.

Let's look at the following example.

val orderDeliveredIntent = Intent(applicationContext, OrderDeliveredActivity::class.java).apply {
   action = ACTION_ORDER_DELIVERED
}
val mutablePendingIntent = PendingIntent.getActivity(
   applicationContext,
   NOTIFICATION_REQUEST_CODE,
   orderDeliveredIntent,
   PendingIntent.FLAG_MUTABLE
)

The PendingIntent here will be passed to our online ordering application. When the transfer is complete, the application can get a customerMessage and return it as an extra of the intent, as shown in the following example:

val intentWithExtrasToFill = Intent().apply {
  putExtra(EXTRA_CUSTOMER_MESSAGE, customerMessage)
}
mutablePendingIntent.send(
  applicationContext,
  PENDING_INTENT_CODE,
  intentWithExtrasToFill
)

The calling application will get the EXTRA_CUSTOMER_MESSAGE Intent and display the message.

to pay special attention to when declaring a variable PendingIntent

⚠️When creating a variable PendingIntent , always explicitly sets the component Intent to be started. It can be operated by our implementation above, that is, explicitly setting the exact class name to be received, but it can also be achieved by Intent.setComponent() ).

Your application may call Intent.setPackage() in some scenarios to make it more convenient. But please pay special attention to this approach may to multiple component . If possible, it is best to specify a specific component.

⚠️If you try to overwrite FLAG_IMMUTABLE created with PendingIntent , then the operation will without any prompt , and pass the original package unmodified Intent .

Remember that the application can always update its own PendingIntent , even if it is an immutable type. The only reason for making PendingIntent a variable type is that other applications need to update the encapsulated Intent some way.

about the tag

We have introduced a few PendingIntent , and some tags are also introduced for everyone.

FLAG_IMMUTABLE : other applications represented by PendingIntent.send () sent to PendingIntent the Intent can not be modified. The application can always use the FLAG_UPDATE_CURRENT tag to modify its own PendingIntent.

In systems prior to Android 12, PendingIntent created without this mark is a variable type by default.

⚠️ In systems prior to Android 6 (API 23), PendingIntent are all variable types.

🆕 FLAG_MUTABLE : Indicates that the intent content passed in by PendingIntent.send() can be merged into the Intent in PendingIntent by the application.

⚠️ For any variable type of PendingIntent, is always set to the encapsulated Intent ComponentName . Failure to take this action may cause safety hazards.

This mark was added in the Android 12 version. In versions prior to Android 12, any PendingIntent created without specifying the FLAG_IMMUTABLE flag is an implicit variable type.

FLAG_UPDATE_CURRENT : Initiate a request to the system to update the existing PendingIntent with new extra data instead of saving the new PendingIntent. If PendingIntent is not registered, register it.

FLAG_ONE_SHOT : Only allow PendingIntent (via PendingIntent.send()) to be sent once. It is very important for the scenario where the internal Intent can only be sent once when the PendingIntent is passed. This mechanism may be easy to operate, or it may prevent the application from performing an operation multiple times.

🔐 Use FLAG_ONE_SHOT to avoid replay attack ".

FLAG_CANCEL_CURRENT : Before registering a new PendingIntent , cancel an PendingIntent . This tag is used when a PendingIntent is sent to an application, and then you want to forward it to another application and update the data in it. After using FLAG_CANCEL_CURRENT , the previous application can no longer call the send method, and the subsequent application can call it.

Receive PendingIntent

In some cases, the system or other frameworks will use PendingIntent as the return value of the API call. A typical example is the method MediaStore.createWriteRequest() , which was added in Android 11.

static fun MediaStore.createWriteRequest(
   resolver: ContentResolver,
   uris: MutableCollection<Uri>
): PendingIntent

PendingIntent created by our application, it runs as our application, while PendingIntent created by the system runs as the system. Specific to the usage scenario of the API here, it allows the application to open Activity and give our application the write permission of the Uri

Summary

In this article we introduced PendingIntent how as Intent the Intent of the packaging system or other applications can be started in as one application at a future date the application is created.

We also introduced PendingIntent needs to be set to immutable, and doing so will not affect the PendingIntent object created by the application to modify itself. This can be achieved by FLAG_UPDATE_CURRENT mark plus FLAG_IMMUTABLE .

We also introduced the PendingIntent that need to be done if 06110cb7599a9f is variable-to ensure that the encapsulated Intent set to ComponentName .

Finally, we introduced how sometimes systems or frameworks provide PendingIntent to applications so that we can decide how and when to run them.

The security of apps has been improved in Android 12, and these updates to PendingIntent complement it well. For more information, please refer to our previous tweet " Android 12 first developer preview version arrives ".

For more information, welcome to use the Android 12 Developer Preview test your application, and tell us your experience.


Android开发者
404 声望2k 粉丝

Android 最新开发技术更新,包括 Kotlin、Android Studio、Jetpack 和 Android 最新系统技术特性分享。更多内容,请关注 官方 Android 开发者文档。