Enviar SMS hasta que tenga éxito

Estoy tratando de enviar un SMS urgente desde mi aplicación. Tengo que asegurarme de que el SMS se envíe con éxito.

El SMS se envía después del arranque del sistema Android y después de que se realiza un control.

Así que tengo una clase de servicio que maneja el filtro de intención BOOT_COMPLETED. Esta clase realiza una comprobación y si algo es cierto, intenta enviar un mensaje SMS a través de otra clase que “extiende el servicio”

Después de asegurarse de que el sms se envía correctamente, ambos servicios (el que maneja la llamada de inicio y el que envía el sms) deben salir.

Pregunta 1 : ¿Cómo hacer que se llame a mi función de envío de sms con un tiempo de espera sin que la aplicación deje de responder? Actualmente estoy usando esto (no sé si es la forma correcta de hacerlo, aunque funciona):

Timer mTimer = new Timer(); //wait a small timeout prior to sending the message. mTimer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { this.cancel(); //I don't want to run the timer more than once sms_sender sms = new sms_sender(); sms.sendSMS(phoneNumber, messageText); } }, 30000, 30000); //run sendSMS() after 30 seconds 

Pregunta 2 : ¿Cómo implementar la función sendSMS para volver a intentar cada 30 segundos después de darse cuenta de que el último bash fue fallido?

Pregunta 3 : ¿Cómo detener ambos servicios una vez que me doy cuenta de que el SMS se envió correctamente?

Este es mi código que no funciona:

 public class sms_sender extends Service { @Override public IBinder onBind(Intent arg0) { // TODO Auto-generated method stub return null; } final String SENT = "SMS_SENT"; public void sendSMS(final String phoneNumber, final String message, final boolean check_result) { PendingIntent sentPI = PendingIntent.getBroadcast(this, 0, new Intent(SENT), 0); registerReceiver(new BroadcastReceiver(){ @Override public void onReceive(Context arg0, Intent arg1) { if(!check_result) return; switch (getResultCode()) { case Activity.RESULT_OK: //exit stopSelf(); return; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: case SmsManager.RESULT_ERROR_NO_SERVICE: case SmsManager.RESULT_ERROR_NULL_PDU: case SmsManager.RESULT_ERROR_RADIO_OFF: //try again in 1 minute Timer mTimer = new Timer(); mTimer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { this.cancel(); //no need to run again, if it fails, this exact code will run again sendSMS(phoneNumber, message, true); } }, 60000, 60000); return; } } }, new IntentFilter(SENT)); SmsManager smsManager = SmsManager.getDefault(); smsManager.sendTextMessage(phoneNumber, null, message, sentPI, null); } } 

Actualmente, el progtwig se bloquea en la llamada PendingIntent. Traté de implementar BroadCastReceiver en el método onCreate utilizando variables de miembros privados para volver a llamar a la función sendSMS () a través del método onReceive, pero el onReceive nunca pareció ejecutarse.

– EDITAR –

Entonces, este es mi código de trabajo final. Supongo que mi caso es especial porque no funciona en un hilo de interfaz de usuario. Tengo un Broadcast Receiver que se ejecuta en Boot. Estoy tratando de enviar un mensaje de texto hasta que se envíe con éxito.

Este Boot Broadcast Receiver inicia un servicio. Este es un código de ella:

 public class service extends Service{ static public service serv; //member variable. Initializing to null so as to know whether to unregister the service or not private BroadcastReceiver messageSent = null; ... ... @Override public void onStart(Intent intent, int startid) { serv=this; //will use this static variable in order to shutdown the service when the message is successfully sent ... ... if(somethingIsTrue()){ //register receiver messageSent = new sent_message(); registerReceiver(messageSent, new IntentFilter(sms_sender.INTENT_MESSAGE_SENT)); startMessageServiceIntent(messageText, phoneNumber); //function code can be found on accepted answer } } } 

La clase send_message es la siguiente:

 public class sent_message extends BroadcastReceiver { private Context pubCon; private void startMessageServiceIntent(String message, String receiver) { Intent i = new Intent(pubCon, sms_sender.class); i.putExtra(sms_sender.EXTRA_MESSAGE, message); i.putExtra(sms_sender.EXTRA_RECEIVERS, new String[] { receiver }); pubCon.startService(i); } @Override public void onReceive(Context context, Intent intent) { pubCon=context; switch (getResultCode()) { case Activity.RESULT_OK: //all went OK, stop the service where this is called from service.serv.stopSelf(); break; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: case SmsManager.RESULT_ERROR_NO_SERVICE: case SmsManager.RESULT_ERROR_NULL_PDU: case SmsManager.RESULT_ERROR_RADIO_OFF: //try sending the message again after 30s new Handler().postDelayed(new Runnable(){ @Override public void run(){ startMessageServiceIntent(service.messageText, service.phoneNumber); } }, 30000); break; } } } 

Y una versión simplificada (solo acepta un receptor) de la clase sms_sender es la siguiente:

 public class sms_sender extends IntentService { public static final String INTENT_MESSAGE_SENT = "message.sent"; public static final String INTENT_MESSAGE_DELIVERED = "message.delivered"; public static final String EXTRA_MESSAGE = "extra.message"; public static final String EXTRA_RECEIVERS = "extra.receivers"; public sms_sender() { super("sms_sender"); } private static class IDGenerator { private static final AtomicInteger counter = new AtomicInteger(); public static int nextValue() { return counter.getAndIncrement(); } } public void sendSMS(String message, String receiver) { SmsManager sm = SmsManager.getDefault(); PendingIntent sentPI = null; Intent sentIntent = new Intent(INTENT_MESSAGE_SENT); int sentID = IDGenerator.nextValue(); sentPI = PendingIntent.getBroadcast(sms_sender.this, sentID, sentIntent, PendingIntent.FLAG_CANCEL_CURRENT); try { sm.sendTextMessage(receiver, null, message, sentPI, null); } catch (IllegalArgumentException e) { System.out.println("Illegal argument"); } } protected void onHandleIntent(Intent intent) { String message = intent.getStringExtra(EXTRA_MESSAGE); String[] receivers = intent.getStringArrayExtra(EXTRA_RECEIVERS); sendSMS(message, receivers[0]); } } 

    Esto es lo que hice:

     public class SMSSender extends IntentService { public static final String INTENT_MESSAGE_SENT = "message.sent"; public static final String INTENT_MESSAGE_DELIVERED = "message.delivered"; public static final String EXTRA_MESSAGE = "extra.message"; public static final String EXTRA_RECEIVERS = "extra.receivers"; public SMSSender() { super("SMSSender"); } private final String TAG = "SendSMS"; private static class IDGenerator { private static final AtomicInteger counter = new AtomicInteger(); public static int nextValue() { return counter.getAndIncrement(); } } private void sendSMS(String message, String[] receivers) { SmsManager sm = SmsManager.getDefault(); ArrayList parts = sm.divideMessage(message); PendingIntent sentPI = null; PendingIntent deliveredPI = null; Intent sentIntent = new Intent(INTENT_MESSAGE_SENT); int sentID = IDGenerator.nextValue(); sentPI = PendingIntent.getBroadcast(SMSSender.this, sentID, sentIntent, PendingIntent.FLAG_CANCEL_CURRENT); Intent deliveryIntent = new Intent(INTENT_MESSAGE_DELIVERED); int deliveredID = IDGenerator.nextValue(); deliveredPI = PendingIntent.getBroadcast(SMSSender.this, deliveredID, deliveryIntent, PendingIntent.FLAG_CANCEL_CURRENT); Log.i(TAG, "sending SMS: parts: " + parts.size() + " message: " + message); if (parts.size() > 1) { ArrayList sentIntents = null; ArrayList deliveredIntents = null; sentIntents = new ArrayList(); deliveredIntents = new ArrayList(); for (int i = 0; i < parts.size(); i++) { sentIntents.add(sentPI); deliveredIntents.add(deliveredPI); } for (String receiver : receivers) { try { sm.sendMultipartTextMessage(receiver, null, parts, sentIntents, deliveredIntents); } catch (IllegalArgumentException e) { Log.e(TAG, "illegal receiver: " + receiver); } } } else { for (String receiver : receivers) { try { sm.sendTextMessage(receiver, null, parts.get(0), sentPI, deliveredPI); } catch (IllegalArgumentException e) { Log.e(TAG, "illegal receiver: " + receiver); } } } } @Override protected void onHandleIntent(Intent intent) { String message = intent.getStringExtra(EXTRA_MESSAGE); String[] receivers = intent.getStringArrayExtra(EXTRA_RECEIVERS); sendSMS(message, receivers); } 

    Y para usarlo:

      private void startMessageServiceIntent(String message, String receiver) { Intent i = new Intent(context, SMSSender.class); i.putExtra(SMSSender.EXTRA_MESSAGE, message); i.putExtra(SMSSender.EXTRA_RECEIVERS, new String[] { receiver }); startService(i) } 

    Observe que admite múltiples receptores, que este método no demuestra / usa.

    Recuerda en tu manifestación:

       

    Opcionalmente puede escuchar cuándo se envían y / o se envían los mensajes:

     @Override protected void onCreate() { ... // ---when the SMS has been sent--- private BroadcastReceiver messageSent; // < - stored as a field messageSent = new SentMessage(); registerReceiver(messageSent, new IntentFilter(SMSSender.INTENT_MESSAGE_SENT)); // ---when the SMS has been delivered--- private BroadcastReceiver messageDelivered; // <- stored as a field messageDelivered = new MessageDelivered(); registerReceiver(messageDelivered, new IntentFilter( SMSSender.INTENT_MESSAGE_DELIVERED)); } @Override protected void onDestroy() { // remember to unregister unregisterReceiver(messageSent); unregisterReceiver(messageDelivered ); } 

    Sé que esto no demuestra respuestas a todas sus preguntas, pero espero que sea suficiente.

    Editar: agregó mis implementaciones de messageSent y messageDelivered

    Estos son específicos de mi implementación, por lo que incluye algunos códigos que no puede usar, simplemente es para demostración.

    Mensaje enviado:

     public class SentMessage extends BroadcastReceiver { private final String TAG = "SentMessage"; @Override public void onReceive(Context context, Intent intent) { long _id = intent.getLongExtra(EXTRA_ID, -1); long protocol_id = intent.getLongExtra(EXTRA_PROTOCOL, -1); Log.d(TAG, "SentMessage"); switch (getResultCode()) { case Activity.RESULT_OK: Log.d(TAG, "RESULT_OK"); if (MessageData.sentMessage(_id, protocol_id)) { try { Database.messageSent(_id); } catch (DatabaseRowNotFoundException e) { Log.e(TAG, e.toString(), e); } } break; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: Log.d(TAG, "RESULT_ERROR_GENERIC_FAILURE"); MessageData.postponeMessage(_id); ApplicationData.hasSignal(false); break; case SmsManager.RESULT_ERROR_NO_SERVICE: Log.d(TAG, "RESULT_ERROR_NO_SERVICE"); MessageData.postponeMessage(_id); ApplicationData.hasSignal(false); break; case SmsManager.RESULT_ERROR_NULL_PDU: Log.d(TAG, "RESULT_ERROR_NULL_PDU"); break; case SmsManager.RESULT_ERROR_RADIO_OFF: Log.d(TAG, "RESULT_ERROR_RADIO_OFF"); MessageData.postponeMessage(_id); ApplicationData.hasSignal(false); break; } } 

    Mensaje enviado:

     public class DeliveredMessage extends BroadcastReceiver { private final String TAG = "DeliveredMessage "; @Override public void onReceive(Context context, Intent intent) { long _id = intent.getLongExtra(EXTRA_ID, -1); long protocol_id = intent.getLongExtra(EXTRA_PROTOCOL, -1); switch (getResultCode()) { case Activity.RESULT_OK: if (_id != -1 && MessageData.deliveredMessage(_id, protocol_id)) { try { Database.messageDelivered(_id); Cursor messageCursor = Database.getCursorByID(MessageOutboxContentProvider.CONTENT_URI, MessageOutboxContentProvider._ID, _id); messageCursor.close(); } catch (DatabaseRowNotFoundException e) { Log.e(TAG, e.toString(), e); } } break; case Activity.RESULT_CANCELED: break; } } 

    }

    También necesitaba un envío confiable, así que guardé referencias a todos los mensajes pendientes en una base de datos, que frecuentemente buscaba mensajes pospuestos. Un mensaje se pospondrá si no hay radio, o el envío simplemente falla por la razón que sea.

    También utilicé GCM junto con SMS para enviar el mensaje lo más rápido posible, enviando mensajes usando ambos canales al mismo tiempo.

    Edit2: Bueno, bien podría abordar las preguntas, de todos modos estamos casi allí:

    Pregunta 1: desde el uso de IntentService, el envío se realiza en segundo plano.

    Solo quiere que el envío se realice una vez después de un retraso, por lo que debe hacer esto en su lugar:

      new Handler().postDelayed(new Runnable() { @Override public void run() { // send sms } }, delay); 

    Pregunta 2: Fácil, cuando la transmisión de mensajes enviados detecta un error, realice el método anterior. Puede agregar información adicional, además del receptor y el mensaje, contando el número de rebashs hasta ahora para que pueda detener el ciclo de envío / rebashs.

    Pregunta 3: el envío se detiene por sí mismo, ya que es un Servicio Intento. En cuanto al otro servicio, el enfoque más simple, creo, sería enviar una transmisión común, que será captada por su actividad principal. De esta forma, puede obtener el servicio en el lugar correcto y detenerlo.