【问题标题】:Do I need to unregister 'anonymous' BroadcastReceiver我是否需要注销“匿名”广播接收器
【发布时间】:2012-06-06 17:24:28
【问题描述】:

我最近问了一个关于检查已发送 SMS 状态的问题,给出的答案是一个代码 sn-p,它注册了两个“匿名内部”(如果不正确,请更正我的术语)BroadcastReceivers 以收听SMS 发送/传递的广播。这些接收器只需要接收有关我的应用程序刚刚发送的 SMS 的数据,因此不需要永久收听。

我的直接想法是“好吧,我需要在完成它们后取消注册它们”,但这是正确的吗?我问了这个海报,因为他没有包含任何注销代码,但没有得到答复。该代码似乎是一种非常标准的方式来做我想做的事情,因为它出现在许多 Android 开发网站上。这里是:

//---sends an SMS message to another device---
private void sendSMS(String phoneNumber, String message)
{        
    String SENT = "SMS_SENT";
    String DELIVERED = "SMS_DELIVERED";

    PendingIntent sentPI = PendingIntent.getBroadcast(this, 0,
        new Intent(SENT), 0);

    PendingIntent deliveredPI = PendingIntent.getBroadcast(this, 0,
        new Intent(DELIVERED), 0);

    //---when the SMS has been sent---
    registerReceiver(new BroadcastReceiver(){
        @Override
        public void onReceive(Context arg0, Intent arg1) {
            switch (getResultCode())
            {
                case Activity.RESULT_OK:
                    Toast.makeText(getBaseContext(), "SMS sent", 
                            Toast.LENGTH_SHORT).show();
                    break;
                case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
                    Toast.makeText(getBaseContext(), "Generic failure", 
                            Toast.LENGTH_SHORT).show();
                    break;
                case SmsManager.RESULT_ERROR_NO_SERVICE:
                    Toast.makeText(getBaseContext(), "No service", 
                            Toast.LENGTH_SHORT).show();
                    break;
                case SmsManager.RESULT_ERROR_NULL_PDU:
                    Toast.makeText(getBaseContext(), "Null PDU", 
                            Toast.LENGTH_SHORT).show();
                    break;
                case SmsManager.RESULT_ERROR_RADIO_OFF:
                    Toast.makeText(getBaseContext(), "Radio off", 
                            Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    }, new IntentFilter(SENT));

    //---when the SMS has been delivered---
    registerReceiver(new BroadcastReceiver(){
        @Override
        public void onReceive(Context arg0, Intent arg1) {
            switch (getResultCode())
            {
                case Activity.RESULT_OK:
                    Toast.makeText(getBaseContext(), "SMS delivered", 
                            Toast.LENGTH_SHORT).show();
                    break;
                case Activity.RESULT_CANCELED:
                    Toast.makeText(getBaseContext(), "SMS not delivered", 
                            Toast.LENGTH_SHORT).show();
                    break;                        
            }
        }
    }, new IntentFilter(DELIVERED));        

    SmsManager sms = SmsManager.getDefault();
    sms.sendTextMessage(phoneNumber, null, message, sentPI, deliveredPI);

代码运行良好。

此外,它不会收到任何发生在我的应用程序外部的 SMS 发送/传递事件的通知。例如。我可以在这些BroadcastReceivers 注册后发送短信,但看不到任何Toast 消息。

所以,我有两个问题:

  1. 我需要注销这些BroadcastReceivers吗?
  2. 如果没有,为什么不呢?

【问题讨论】:

    标签: android sms broadcastreceiver anonymous-inner-class


    【解决方案1】:

    只需将您的 BroadcastReceiver 保存到一个实例中,您就可以取消注册它;-)

    只需将这一行更改为:

    registerReceiver(new BroadcastReceiver(){
    

    到:

    BroadcastReceiver smsReceiver=new BroadcastReceiver(){...}
    registerReceiver(smsReceiver);
    

    稍后你可以执行比:

    unregisterReceiver(smsReceiver);
    

    请记住将smsReceiver 保存为班级成员。

    【讨论】:

    • 我认为我不需要将 BroadcastReceiver 保存到实例中 - 如果需要,我可以在 onReceive() 结束时取消注册。我只是想知道是否有必要。正如我所说,这段代码散布在互联网上,没有取消注册!
    【解决方案2】:

    此外,它不会收到在我的应用程序外部发生的任何 SMS 发送/传递事件的通知。

    您正在动态注册接收器。如果你想监听其他应用程序的意图,你需要在清单文件中注册你的接收器。这样,它将始终处于活动状态。而且您无需取消注册。

    【讨论】:

      【解决方案3】:

      您需要取消注册。否则,它很可能会崩溃。我对此非常确定。但是如果你说它没有崩溃,那么,好吧,需要调查一下:)

      因为,Android 会认为您忘记取消注册。它希望您取消注册。 “注意:如果在 Activity.onResume() 实现中注册接收器,则应在 Activity.onPause() 中取消注册。(暂停时不会收到意图,这将减少不必要的系统开销)。不要在 Activity.onSaveInstanceState() 中取消注册,因为如果用户移回历史堆栈,则不会调用它。”

      【讨论】:

      • 你认为它为什么会崩溃?
      • 好吧,代码在不同的 BroadcastReceiver 中(由 AlarmManager 唤醒),所以我没有任何生命周期方法来取消注册。如有必要,我会将注销 ia 放在 onReceive() 的末尾
      • 这可能存在同步问题,即您的接收器可能在实际接收到某些内容之前就被取消注册。
      • 嗯...如果我在 onReceive() 结束时取消注册,必须调用 onReceive()。 IE。它一定收到了什么……
      • 好吧,您的 onReceive 被调用,SMS 被发送,在您取消注册接收器之前,可能尚未发送交付或发送的广播。没有保证。
      【解决方案4】:

      所以如果我理解你写的正确的话,看起来BroadcastReceiver 应该只存在于sendSMS() 调用中,所以即使它们注册了SENTDELIVERED Intents,它们也可能不在附近接收它们。另外,我相信您在完成接收器后总是想取消注册它们,我已经看到调试器警告接收器泄漏。

      如果你有像 sent_broadcast_receiverdelivered_broadcast_receiver 这样的类参数,其中包含对你正在注册的接收者的引用,这样对象会持续存在,并且可以在完成后取消注册,这样会更好吗?

      编辑: 无论对象持有 SENTDELIVERED 的 BroadcastReceivers 都应该持续到 Intent 返回,或者 Intent(因为它们是 PendingIntent)应该指向可以被唤醒的东西,如果这是你想要的。

      【讨论】:

      • SMS 广播接收器是在由 AlarmManager 调用的不同广播接收器中创建的 - 即发生警报,​​我想发送一条短信,因此在注册的广播接收器中执行代码 sn-p警报管理器。您建议将接收器作为变量存储在此接收器中,以便它们持续存在,但是如果广播发生时警报接收器不在身边怎么办?我需要服务吗?
      • 我指的不是您在 AlarmManager 中注册的内容,而是“新的 BroadcastReceiver”构造函数。实际上,您的代码中似乎没有任何注册 AlarmManager。它为 SENT 和 DELIVERED 创建两个 PendingIntent,为这些 Intent 注册方法范围 BroadcastReceivers,然后使用 SmsManager。
      • 很抱歉,您的编辑对我来说没有意义 - “...或者 Intents(因为它们是 PendingIntents)如此直接地指向可以被唤醒的东西,如果这就是你的话欲望。”。你能进一步解释一下吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多