【问题标题】:How to get the sender of an Intent?如何获取 Intent 的发送者?
【发布时间】:2022-04-04 14:46:57
【问题描述】:

Activity 有没有办法找出谁(即班级名称)发送了Intent?我正在为我的Activity 寻找一种通用的方式来响应收到的意图,将其发回给发件人,无论是谁。

【问题讨论】:

    标签: android


    【解决方案1】:

    可能还有另一种方法,但我知道的唯一解决方案是让 Activity A 通过 startActivityForResult() 调用 Activity B。然后 Activity B 可以使用getCallingActivity() 来检索 Activity A 的身份。

    【讨论】:

    • 我们正在开发一个库,用于验证Intents 的发送者和接收者,它首先使用这种技术,因为它是最简单的。你可以在这里找到更多dev.guardianproject.info/projects/trustedintents/wiki
    • 如何使用getCallingActivity()?我试图用它来设置这样的文本:txt.setText(getCallingActivity()); 但它显示'null'。我该怎么做才能让它显示调用活动的名称?
    • 如果调用活动不期待结果(即它没有使用包含请求代码的 startActivityForResult 表单),那么调用包将为空
    【解决方案2】:

    它是您接收意图的外部应用程序吗?你可以使用activity类的getReferrer()方法

    一个简单的例子:我打开了谷歌地图应用程序,通过使用谷歌地图的分享选项与我的应用程序分享一些位置。然后我的应用程序打开并在 Activity 中调用此方法:

     this.getReferrer().getHost()
    

    将返回:

     com.google.android.apps.maps
    

    在此处查看文档:https://developer.android.com/reference/android/app/Activity.html#getReferrer()

    请注意,这需要 API 22。对于较旧的 Android 版本,请参阅 ajwillliams 的回答

    【讨论】:

    • 如果您的应用程序已经打开并且在您使用谷歌地图中的共享功能时仍处于后台,该怎么办。它还会返回谷歌地图的包名吗?
    • getReferrer() 需要 API22 (Android 5.1)。在 4.4 等较低版本中该怎么办?
    • 一些检查 API22 的代码:String startedFrom = null; if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { startedFrom = this.getReferrer().getHost(); }
    【解决方案3】:

    我使用的一种技术是要求发送相关 Intent 的应用程序将 PendingIntent 添加为 Parcelable extra; PendingIntent 可以是任何类型(服务、广播等)。我的服务唯一要做的就是调用 PendingIntent.getCreatorUid() 和 getCreatorPackage();此信息在创建 PendingIntent 时填充,并且无法由应用程序伪造,因此我可以获得有关 Intent 发件人的信息。 唯一需要注意的是,该解决方案仅适用于 Jellybean 和后来的情况,这是我的情况。 希望这会有所帮助,

    【讨论】:

    • 您如何要求他们发送 PendingIntent?我没有看到一个意图过滤器选项。您是否只是丢弃没有额外的意图?
    • 乍一看似乎是个坏主意。恶意应用程序不能伪造PendingIntent 吗?当然,PendingIntent 没有公共构造函数,但 readPendingIntentOrNullFromParcel 看起来可以解决问题,不是吗?
    • @Giszmo 以及 getCreatorUid() 文档声明它不能被欺骗。不确定如何处理序列化,但必须有一些特殊的安全措施,例如使用创建者拥有的私钥对序列化内容进行签名。也就是说,正如 getCreatorUid() 文档指出的那样,您仍然必须小心这种策略。仅仅因为某些包 X 具有由 Y 创建的 PendingIntent 并不意味着 X == Y。Y 可能出于某些不相关的目的将 PendingIntent 发送到 X。 Y 向 X 发送 PendingIntent 意味着 Y 信任 X 执行该特定操作,而不是一般情况下。
    【解决方案4】:

    这不是非常直接,但您可以从 ActivityManager 获取最近任务的列表。所以调用者本质上是你之前的任务,你可以获取关于该任务的信息。

    示例用法:

    ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
    List<ActivityManager.RecentTaskInfo> recentTasks = am.getRecentTasks(10000,ActivityManager.RECENT_WITH_EXCLUDED);
    

    上面将返回从最近(您的)到指定限制的所有任务的列表。请参阅文档here,了解可以从 RecentTaskInfo 对象中获取的信息类型。

    【讨论】:

    【解决方案5】:

    通常您不需要知道这一点。如果调用活动使用startActivityForResult(Intent, int),则被调用者可以使用setResult(int, Intent) 指定要发送回调用者的Intent。调用者将在其onActivityResult(int, int, Intent) 方法中收到此 Intent。

    【讨论】:

    • setResult 的意图仅在子 Activity 关闭后发送。我希望能够在子活动运行时任意将意图发送回父活动。
    • @zer0stimulus 您可以创建一个在后台运行的应用服务来解析结果。
    【解决方案6】:

    根据您的问题,由于您想向发件人发送意图返回,因此 startActivityForResult 是比我建议的更好的选择。但是我需要在用户单击通知时启动活动 B,并在活动 B 中执行一些代码只有当发送者活动是活动 A 时。这就是我非常简单的做法。

    内部活动 A:

    String senderName = this.getClass().getSimpleName();        
    Intent clickIntent = new Intent(ActivityA.this, ActivityB.class);
    clickIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    clickIntent.putExtra("SENDER_CLASS_NAME", senderName);
    
    //I use PendingIntent to start Activity B but you can use what you like such as this.startActivity(clickIntent);
    PendingIntent.getActivity(ActivityA.this, NOTIFICATION_ID, clickIntent, PendingIntent.FLAG_ONE_SHOT);
    

    活动 B 内部:

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        if (savedInstanceState == null) {
            Bundle bundle = getIntent().getExtras();
            if (bundle != null) {
                if(bundle.containsKey("SENDER_CLASS_NAME")){
                    String senderName = bundle.getString("SENDER_CLASS_NAME");                  
                    //Execute some code
                    Log.d("GCM", "Notifications clicked");
                    }
                }
            }
        }
    

    【讨论】:

      【解决方案7】:

      就我而言,这里接受的和另一个投票最多的答案都不是完美的。

      Activity.getCallerActivity() 仅适用于通过 startActivityForResult 启动您的活动的发送者,这意味着如果发件人也在您的应用中并且您拥有完全控制权,则它可以工作,但并非每个外部应用都以这种方式启动其他应用。

      另一个投票最多的答案为外部应用程序提供了解决方案,但它也有问题。首先,我更喜欢 getAuthority() 而不是 getHost(),其次,如果发件人是浏览器类型应用程序,例如 Chrome,主机和权限都会给您浏览网页的地址主机,例如 www.google.com,而不是应用程序本身。所以这取决于你如何定义'发送者',如果你需要找出哪个网页启动你,权限/主机足够好,但如果你需要找出哪个应用程序启动你,恐怕权限/主机可以只有当 getScheme() 为您提供 android-app 而不是 http

      【讨论】:

        【解决方案8】:

        使用 UsageStatsManager 和旧的 RecentTaskInfo 获取 OnCreate 或 onNewIntent 的意图发送者:

            public static String getTopMostThirdPartyPackage(Context context) {
                String thisPak = null, tmp, top = null;
                try {
                    thisPak = context.getPackageName();
                    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
                        UsageStatsManager man = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
                        long now = System.currentTimeMillis();
                        UsageEvents uEvts = man.queryEvents(now - 5000,now); // query in 5 sec
                        UsageEvents.Event e = new UsageEvents.Event();
                        while (uEvts.getNextEvent(e)){
                            tmp = e.getPackageName();
                            if (!thisPak.equals(tmp)) {
                                top = tmp;
                                break;
                            }
                        }
                    } else {
                        ActivityManager man = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
                        List<ActivityManager.RecentTaskInfo> tasks = man.getRecentTasks(3, 0);
                        for(ActivityManager.RecentTaskInfo info:tasks) {
                            tmp = info.baseIntent.getComponent().getPackageName();
                            if (!thisPak.equals(tmp)) {
                                top = tmp;
                                break;
                            }
                        }
                    }
                } catch (Exception e) {
                }
                return top;
            }
        

        权限:

            <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
            <uses-permission android:name="android.permission.GET_TASKS" />
        
                intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
                startActivity(intent);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-10-26
          • 2011-11-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多