PendingIntent使用解析

PendingIntent 中的单词 “pending” 指延迟或挂起,就是指它是延迟的或挂起的。一句话概括,PendingIntent 一种是支持授权其他应用以当前应用的身份执行包装的 Intent 操作的系统特性。

什么场景下会使用 PendingIntent?

场景 1 – 系统通知消息的点击操作
场景 2 – 桌面Widget的点击操作
场景 3 – 系统闹钟操作
场景 4 – 第三方应用回调操作

PendingIntent 和 Intent 有什么区别?

从结构上来说,PendingIntent 是 Intent 的包装类,其内部持有一个代表最终意图操作的 Intent。

1、执行进程不同 —— PendingIntent 在其他进程执行: Intent 通常会在创建进程中执行,而 PendingIntent 通常不会在创建进程中执行;
2、执行时间不同 —— PendingIntent 会延迟执行: Intent 通常会立即执行,而 PendingIntent 通常会延迟执行,延迟到其他进程完成任务后再执行,甚至延迟到创建进程消亡后。例如,在 场景 1 – 系统通知消息的点击操作 中,即使发送系统通知消息的进程已经消亡了,依然不妨碍二级 Intent 的跳转;
3、执行身份不同 —— PendingIntent 支持授权: PendingIntent 内部持有授权信息,支持其他应用以当前应用的身份执行,这有利于避免嵌套 Intent 存在的安全隐患。

PendingIntent 的使用方法

启动组件:

// 启动 Activity
PendingIntent.getActivity(Context context, int requestCode, Intent intent, int flags)
// 启动 Service
PendingIntent.Service(Context context, int requestCode, Intent intent, int flags)
// 发送广播
PendingIntent.getBroadcast(Context context, int requestCode, Intent intent, int flags)
PendingIntent 的 4 个参数:

1、context: 当前应用的上下文,PendingIntent 将从中抽取授权信息;
2、requestCode: PendingIntent 的请求码,与 Intent 的请求码类似;
3、intent: 需要跳转的包装intent;
4、flag: PendingIntent标记位

PendingIntent标记位

FLAG_IMMUTABLE:不可变标记位,将约束外部应用消费 PendingIntent 修改其中的 Intent;
FLAG_MUTABLE:可变标记位,不约束外部应用消费 PendingIntent 修改其中的 Intent;
FLAG_UPDATE_CURRENT:如果系统中已经存在相同的 PendingIntent,那么将保留原有 PendingIntent 对象,而更新其中的 Intent。即使不可变 PendingIntent,依然可以在当前应用更新;
FLAG_CANCEL_CURRENT:如果系统中已经存在相同的 PendingIntent,那么将先取消原有的 PendingIntent,并重新创建新的 PendingIntent。
FLAG_NO_CREATE:如果系统中已经存在相同的 PendingIntent,那么不会重新创建,而是直接返回 null;
FLAG_ONE_SHOT:一次有效标记位,PendingIntent 被消费后不支持重复消费,即只能使用一次。

PendingIntent 可变性是一种对外部应用消费行为的约束机制,通过标记位 FLAG_MUTABLE 和 FLAG_IMMUTABLE 控制 PendingIntent 可变或不可变。可变性意味着在消费 PendingIntent 时,可以针对其中包装的 Intent 进行修改。强烈建议在创建 PendingIntent 时使用 FLAG_IMMUTABLE。

完整使用示例:
            val intent = Intent()
            intent.action = "xxxaction"
            intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
            var flag = PendingIntent.FLAG_UPDATE_CURRENT
            if (android.os.Build.VERSION.SDK_INT >= 31) {
                flag = flag or PendingIntent.FLAG_IMMUTABLE
            }
            PendingIntent.getActivity(
                context,
                appWidgetId,
                intent,
                flag
            )

PendingIntent 延后启动 Activity/Service/Broadcast分析

省流:

client 封装真实intent到 PendingIntent ->
真正执行时 PendingIntent.send -> PendingIntentRecord.send & sendInner
-> ATMS.startActivityInPackage/startActivitiesInPackage/sendActivityResult/broadcastIntentInPackage/startServiceInPackage
即 xxxInPackage

1. 概念

PendingIntent:Intent的封装。
把这个封装好的intent交给别的程序,
别的程序根据这个intent延后处理intent中所描述的事情。

2. 三个方法:

getActivity(Context context, int requestCode, Intent intent, int flags, Bundle options)
getBroadcast(Context context, int requestCode, Intent intent, int flags)
getService(Context context, int requestCode, Intent intent, int flags)

3. PendingIntentRecord 向AMS 发起启动请求

最后通过 PendingIntentRecord 向AMS 发起启动请求, 调用AMS的对应方法 xxxInPackage(如startActivityInPackage)

源码:
frameworks/base/services/core/java/com/android/server/am/PendingIntentRecord.java

    public int sendInner(int code, Intent intent, String resolvedType, IBinder allowlistToken,
            IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo,
            String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) {
            
            switch (key.type) {
                case ActivityManager.INTENT_SENDER_ACTIVITY:
                    ...             
                        if (key.allIntents != null && key.allIntents.length > 1) {
                            res = controller.mAtmInternal.startActivitiesInPackage(
                            ...
                        } else {
                            res = controller.mAtmInternal.startActivityInPackage(uid, callingPid,
                    ...         
                case ActivityManager.INTENT_SENDER_BROADCAST:
                        int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName,
                    ... 
                case ActivityManager.INTENT_SENDER_SERVICE:
                case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE:
                        controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType,                

其中: controller 是 PendingIntentController 对象,它的构造函数里,会去获取倒 ATMS 的本地代理对象, 即 ActivityTaskManagerInternal 对象

    PendingIntentController(Looper looper, UserController userController,
            ActivityManagerConstants constants) {
        mH = new Handler(looper);
        mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
        mUserController = userController;
        mConstants = constants;
    }

ActivityTaskManagerInternal 为 ATMS 的本地接口(system_server进程内部使用)

/**
 * Activity Task manager local system service interface.
 * @hide Only for use within system server
 */
public abstract class ActivityTaskManagerInternal { ...}

ActivityTaskManagerService 内部类 LocalService 实现了 ActivityTaskManagerInternal的接口,
并把LocalService 的对象添加到 LocalServices。

public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
    public ActivityTaskManagerService(Context context) {
        ...
        mInternal = new LocalService();
        ...

    public ActivityTaskManagerInternal getAtmInternal() {
        return mInternal;
    }
    
    private void start() {
        LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
    }
    
    final class LocalService extends ActivityTaskManagerInternal {
        @Override
        public int startActivityInPackage(int uid, int realCallingPid, int realCallingUid..) {
            return getActivityStartController().startActivityInPackage(...)
        }
        @Override
        public int startActivityAsUser(IApplicationThread caller....) {
            return ActivityTaskManagerService.this.startActivityAsUser(     
    }   

因此, system_server 其它地方则可以通过 LocalServices.getService(ActivityTaskManagerInternal.class)
去获取到 ActivityTaskManagerInternal 的对象,
进而访问到 ATMS 或者直接处理。

4.参考:

PendingIntent源码分析: https://blog.csdn.net/konan1027/article/details/46914715
PendingIntentRecord 源码:
https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/am/PendingIntentRecord.java?hl=zh-cn

Android中pendingIntent的深入理解:https://blog.csdn.net/yuzhiboyi/article/details/8484771
Intent和PendingIntent的区别
a. Intent是立即使用的,而PendingIntent可以等到事件发生后触发,PendingIntent可以cancel
b. Intent在程序结束后即终止,而PendingIntent在程序结束后依然有效
c. PendingIntent自带Context,而Intent需要在某个Context内运行
d. Intent在原task中运行,PendingIntent在新的task中运行

5. 一个PendingIntent 执行的调用栈:

Client 调用AMS.sendIntentSender -> PendingIntentRecord.sendInner -> ActivityTaskManagerService.startActivityInPackage
->ActivityStartController.startActivityInPackage
-> ActivityStarter.execute/executeRequest
则会执行 start u

07-06  18:49:42.134  E  1876   5896      at com.android.server.wm.ActivityStarter.executeRequest(ActivityStarter.java:1197)
07-06  18:49:42.134  E  1876   5896      at com.android.server.wm.ActivityStarter.execute(ActivityStarter.java:1026)
07-06  18:49:42.134  E  1876   5896      at com.android.server.wm.ActivityStartController.startActivityInPackage(ActivityStartController.java:425)
07-06  18:49:42.134  E  1876   5896      at com.android.server.wm.ActivityTaskManagerService$LocalService.startActivityInPackage(ActivityTaskManagerService.java:6926)
07-06  18:49:42.134  E  1876   5896      at com.android.server.am.PendingIntentRecord.sendInner(PendingIntentRecord.java:586)
07-06  18:49:42.134  E  1876   5896      at com.android.server.am.ActivityManagerService.sendIntentSender(ActivityManagerService.java:6579)
07-06  18:49:42.134  E  1876   5896      at com.android.server.am.ActivityManagerService.sendIntentSender(ActivityManagerService.java:6568)
07-06  18:49:42.134  E  1876   5896      at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:4856)
07-06  18:49:42.134  E  1876   5896      at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3135)
07-06  18:49:42.134  E  1876   5896      at android.os.Binder.execTransactInternal(Binder.java:1316)
07-06  18:49:42.134  E  1876   5896      at android.os.Binder.execTransact(Binder.java:1280)

6. 获取PendingIntent 对象 源码

以获取Activity 的PI 对象为例,
可见有两个方法,分别为 getActivity 和 getActivityAsUser, 前者是公开,后者是hide的。
后者可以传入 当前 user。
前者默认的user 是通过传入的 context 去获取的。

    @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
    public static PendingIntent getActivity(Context context, int requestCode,
            @NonNull Intent intent, @Flags int flags, @Nullable Bundle options) {
        // Some tests only mock Context.getUserId(), so fallback to the id Context.getUser() is null
        final UserHandle user = context.getUser();
        return getActivityAsUser(context, requestCode, intent, flags, options,
                user != null ? user : UserHandle.of(context.getUserId()));
    }

    /**
     * @hide
     * Note that UserHandle.CURRENT will be interpreted at the time the
     * activity is started, not when the pending intent is created.
     */
    @UnsupportedAppUsage
    public static PendingIntent getActivityAsUser(Context context, int requestCode,
            @NonNull Intent intent, int flags, Bundle options, UserHandle user) {
        String packageName = context.getPackageName();
        String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver());
        checkFlags(flags, packageName);
        try {
            intent.migrateExtraStreamToClipData(context);
            intent.prepareToLeaveProcess(context);
            IIntentSender target =
                ActivityManager.getService().getIntentSenderWithFeature(
                    INTENT_SENDER_ACTIVITY, packageName,
                    context.getAttributionTag(), null, null, requestCode, new Intent[] { intent },
                    resolvedType != null ? new String[] { resolvedType } : null,
                    flags, options, user.getIdentifier());
            return target != null ? new PendingIntent(target) : null;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/20807,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?