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

2

I have an application that sends sms to android, and a Broadcast to catch the errors if I can not send to the recipient.  If you can not send a message, I will capture the object and save it to the database with the type of error that occurred, for later upload.

The method works perfectly if I send one at a time, but I ran a test battery where I have more than 50 messages for sending at a time, and the method always returns me the last object in the list of those that gave error. p>

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

1 - Recipient A

2 - Recipient B

3 - Recipient C

4 - Recipient D

Broadcast writes 4 times in the database to element 4.

I pass the object through Intent Extras, when I create the Broadcast, and retrieve it 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 found in tests, is that if I for a Thread.sleep of 6 seconds for example, it writes right, however I have situations that I need to send more than 200 sms at a time, which ends up leaving the screen someone has an idea of what it might be?

// Code that registers 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 a parameter, after the sendMultipartTextMessage method, I added a pause of 6 seconds, and the method started to work, 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 (), that way it worked without any problems.

//OnReceive

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

    
asked by anonymous 27.02.2016 / 01:18

1 answer

2

This behavior has two causes:

  • The BroadcastReceive is being called several times because it is being recorded for each message sent.

  • The fact that only the latter is handled is due to the fact that, when a PendingIntent is requested, the system checks if there is any previously created one and if two equivalent 1) , as per Intent.filterEquals , the same PendingIntent is returned for both.

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

    By passing FLAG_UPDATE_CURRENT we indicate that if PendingIntent already exists, it is kept having its extra replaced with the new Intent . This is useful when you only want to treat Intent with the most recent Extra .

The first one is resolved by registering the BroadcastReceive in onResume() and "unregistering" in onPause() .

In order to resolve the second, we must ensure that the Intent used to obtain the PendingIntent are different (2) , changing one of the aspects considered by the Intent .filterEquals , or the second parameter passed to the PendingIntent.getBroadcast() method is different from the previous one.

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

> class , and categories are the same. The extra is not taken into account.

Convert into a String containing its URI representation with a Schema equal to "intent ".
The tests performed only work in whose extras are primitive types. With Parcelable or Serializable types the Intent were considered equivalent.

    
01.03.2016 / 18:18