Why is Broadcastreceiver called several times and always with the same "extra"?

Asked

Viewed 280 times

2

I have an app that sends text messages on android, and a Broadcast to capture errors if I can’t send to the recipient. If you cannot send a message, I capture the object and write to the database with the type of error that occurred, for later sending.

The method works perfectly if I send one at a time, but I performed a battery of tests where I have more than 50 messages to send at a time, and the method always returns me the last object in the list of those who gave error.

Example, if I send the following sms, and disable airplane mode to simulate an error, in that order:

1 - Consignee A

2 - Consignee B

3 - Consignee C

4 - Consignee D

Broadcast records 4 times in the database element 4.

I pass the object through Extras of Intent, when I create Broadcast, and recover in the code below.

 @Override
public void onReceive(Context ctx, Intent intent) {

    if (naoEnviadoDao == null) {
        naoEnviadoDao = ORMLiteHelper.getInstance(ctx).getNaoEnviadosDao();
    }
   //Recupero o objeto passado como parâmetro no envio
   //Se eu mandar um sms por vez, funciona, mas se for mais 3-4 por exemplo, sempre grava o último n vezes.
    NaoEnviado naoEnviado = (NaoEnviado) intent.getSerializableExtra("naoEnviado");
    if (!naoEnviados.contains(naoEnviado)) {
        naoEnviados.add(naoEnviado);
    }
    switch (getResultCode()) {
        case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
            naoEnviado.setTipoFalha("Falha genérica.");
            break;
        case SmsManager.RESULT_ERROR_NO_SERVICE:
            naoEnviado.setTipoFalha("Sem serviço.");
            break;
        case SmsManager.RESULT_ERROR_NULL_PDU:
            naoEnviado.setTipoFalha("Falha no Provedor PDU.");
            break;
        case SmsManager.RESULT_ERROR_RADIO_OFF:
            naoEnviado.setTipoFalha("Modo avião ativo.");
            break;
    }

    if (naoEnviado.getTipoFalha() != null &&
            !naoEnviado.getTipoFalha().equals("")) {
        try {
            naoEnviadoDao.create(naoEnviado);
        } catch (Exception e) {
            e.printStackTrace();
        }
        naoEnviado = new NaoEnviado();
    }
}

Another detail that I noticed in tests, is that if I put a Thread.Sleep of 6 seconds for example, it records right, but I have situations that I need to send more than 200 sms at a time, which ends up leaving the screen stuck, someone has an idea of what it might be?

//Code I register the Broadcast

 public void sendSMS(final String mensagem, final String nomeParceiro, String telefone) {
        //Remove caracteres do cel e formata a msg antes de enviar...
        Telefone tel = new Telefone(ctx);
        final String celular = tel.formataTelefone(telefone);
        final String msg = mensagem.replace("%nome%", nomeParceiro.substring(0,
                nomeParceiro.indexOf(" ") > 0 ? nomeParceiro.indexOf(" ") : nomeParceiro.length()));

        SmsManager smsManager = SmsManager.getDefault();

        SmsManager sms = SmsManager.getDefault();
        ArrayList<String> parts = sms.divideMessage(msg);
        int messageCount = parts.size();

        ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>();
        ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>();

        NaoEnviado naoEnviado = new NaoEnviado();
        naoEnviado.setMensagem(msg);
        naoEnviado.setNome(nomeParceiro);
        naoEnviado.setTelefone(celular);

        Intent itSent = new Intent(SENT);
        itSent.putExtra("naoEnviado", naoEnviado);

        Intent itDelivery = new Intent(DELIVERED);
        itDelivery.putExtra("naoEnviado", naoEnviado);

        PendingIntent sentPI = PendingIntent.getBroadcast(ctx, 0, itSent,
                PendingIntent.FLAG_UPDATE_CURRENT);

        PendingIntent deliveredPI = PendingIntent.getBroadcast(ctx, 0, itDelivery,
                PendingIntent.FLAG_UPDATE_CURRENT);

        for (int j = 0; j < messageCount; j++) {
            sentIntents.add(sentPI);
            deliveryIntents.add(deliveredPI);
        }

        //Registra os receiver de envio e recebimento
        ((Activity) ctx).registerReceiver(SentReceiver.getInstance(),
                new IntentFilter(SENT));

        ((Activity) ctx).registerReceiver(DeliveredReceiver.getInstance(),
                new IntentFilter(DELIVERED));

        sms.sendMultipartTextMessage(tel.removeCaracteres(celular),
                null, parts, sentIntents, deliveryIntents);
    }

I found two possible solutions:

1) If you want to pass an Object as parameter, after the sendMultipartTextMessage method, I added a 6 seconds pause, and the method started working, it is not an elegant solution, but solved.

//Envia o SMS
    sms.sendMultipartTextMessage(telefone, null, parts, sentIntents, deliveryIntents);
    try {
        Thread.sleep(6000);
    } catch (InterruptedException e) {
    }

2) Instead of passing an object as a parameter, I passed a String, and retrieved it in Onreceive() as getStringExtra(), so it worked without further problems.

//OnReceive

String aux = Intent.getStringExtra("obj");

1 answer

2


This behavior has two causes:

  • The Broadcastreceveir is being called several times because it is being logged by each message sent.

  • The fact that only the latter is treated is due to the fact that when a Pendingintent is requested, the system checks whether there is already one previously created and, if two are used Intent equivalents(1), in accordance with Intent.filterEquals, the same Pendingintent is returned for both.

    To flag passed in the last parameter allows you to "adjust" this behavior:

    As we passed FLAG_UPDATE_CURRENT indicate that if the Pendingintent already exists, it is kept having its extra replaced by the new Intent.
    This is useful when you only want to treat once the Intent with the Extra most recent.

The first is resolved by registering Broadcastreceveir in the onResume() and "de-registering" in onPause().

To solve the second we have to ensure that either Intent used to obtain Pendingintent are different(2), amending one of the aspects considered by Intent.filterEquals, or the second parameter passed to the method PendingIntent.getBroadcast() is different from the previous.

So instead of passing 0 in the second parameter, change the code so that when requesting the Pendingintent, a different value is used for each call, for example (int)System.currentTimeMillis();.

(1)They are equivalent when the action, date, type, class, and Categories are the same. No account is taken of the extra.

(2)One possible way is to define the date in this way: itSent.setData(Uri.parse(itSent.toUri(Intent.URI_INTENT_SCHEME)));

itSent.toUri(Intent.URI_INTENT_SCHEME) converts the Intent in a String containing its representation in URI format with a Schema equal to "Intent".
The tests carried out only work in the extras are primitive types. With Parcelable or Serializable types Intent were considered equivalent.

  • Exactly that, it took a little bit of work to find the solution, but it was solved, thank you very much for the help @ramaral

  • You noticed that the solution passes by using a different value in the second parameter of PendingIntent.getBroadcast()?

  • Yes, but for me I just need to pass the phone number through Intent by parameter, in the case of a String, instead of passing the id as requestcode, the simple fact of using primitive types also solved the problem.

  • 1

    That’s why in my tests the use of setData() worked and with you it didn’t. In my test I used a string as extra!

  • 1

    Yes, exactly, I noticed the problem, when I redid the code and used a is, i as an extra, the same example that you tested, and it worked. then I just adapted my code with this.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.