发送短信直到成功 [英] Send SMS until it is successful

查看:48
本文介绍了发送短信直到成功的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从我的应用程序发送一条紧急短信.我必须确保短信发送成功.

I am trying to send an urgent SMS from my application. I have to make sure that the SMS is being sent successfully.

Android 系统启动后,经过检查后发送短信.

The SMS is being sent after the boot of the Android system and after a check is being made.

所以我有一个处理 BOOT_COMPLETED 意图过滤器的服务类.此类进行检查,如果某事为真,则它会尝试通过另一个扩展服务"类发送 SMS 消息

So I have a service class that handles the BOOT_COMPLETED intent-filter. This class makes a check and if something is true then it tries to send an SMS message via another class that "extends Service"

在确保成功发送短信后,两个服务(处理启动调用的服务和发送短信的服务)必须退出.

After it makes sure that the sms is successfully sent, both services (the one that handles the boot call and the one that sends the sms) must exit.

问题 1:如何使我的 sms 发送函数在超时的情况下被调用,而不会收到应用程序无响应的消息?目前我正在使用这个(我不知道这是否是正确的方法,尽管它有效):

Question 1: How to make my sms sending function be called with a timeout without getting the application being unresponsive message? Currently I am using this (I don't know if it is the correct way to do it, though it works):

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

问题2:如何实现sendSMS功能,在意识到上次尝试失败后每30秒重试一次?

Question 2: How to implement the sendSMS function so as to retry every 30 seconds after realizing that the last attempt was a fail?

问题3:发现短信发送成功后,如何停止这两项服务?

Question 3: How to stop both services after I realize that the SMS was successfully sent?

这是我的代码不起作用:

This is my code which does not work:

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);
    }
}

目前程序在 PendingIntent 调用时崩溃.我尝试使用私有成员变量在 onCreate 方法上实现 BroadCastReceiver,以便通过 onReceive 方法再次调用 sendSMS() 函数,但 onReceive 似乎从未运行过.

Currently the program crashes on the PendingIntent call. I tried to implement the BroadCastReceiver on the onCreate method using private member variables so as to call the sendSMS() function again through the onReceive method, but the onReceive never seemed to run.

-- 编辑 --

所以,这是我最终的工作代码.我想我的情况很特殊,因为它不适用于 UI 线程.我有一个在启动时运行的广播接收器.我正在尝试发送短信,直到成功发送.

So, this is my final working code. I guess that my case is special because it doesn't work on a UI thread. I have a Broadcast Receiver that runs on Boot. I am trying to send an SMS message until it is successfully sent.

这个引导广播接收器启动一个服务.这是它的一些代码:

This Boot Broadcast Receiver starts a service. This is some code from it:

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
        }
    }
}

sent_message 类如下:

The sent_message class is the following:

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;
        }

    }
}

sms_sender 类的简化版本(仅接受一个接收者)如下:

And a simplified (accepts only one receiver) version of the sms_sender class is the following:

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]);
    }
}

推荐答案

这是我所做的:

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<String> 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<PendingIntent> sentIntents = null;
        ArrayList<PendingIntent> deliveredIntents = null;

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

        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);

}

并使用它:

    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)
    }

注意它支持多个接收器,此方法不演示/使用.

Notice it supports multiple receivers, which this method does not demonstrate/use.

在你的清单中记住:

<uses-permission android:name="android.permission.SEND_SMS" />

<service android:name="your.package.SMSSender" android:enabled="true" />

您可以选择在发送和/或传递消息时进行监听:

Optionally you can listen for when messages are sent and/or delivered:

@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 );
}

我知道这并不能说明您所有问题的答案,但我希望这已经足够了.

I know this does not demonstrate answers to all your questions but I hope that it is sufficient.

添加了我对 messageSent 和 messageDelivered 的实现

Added my implementations of messageSent and messageDelivered

这些是我的具体实现,所以包含了一些你不能使用的代码,只是为了演示.

These are specific to my implementation, so includes some code that you cannot use, it is simply for demonstration.

发送的消息:

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;
    }

}

已发送消息:

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;
    }
}

}

我也需要可靠的发送,所以在数据库中保留对所有待处理消息的引用,我会经常扫描延迟消息.如果没有无线电,消息将被推迟,或者由于任何原因发送失败.

I was in the need for reliable sending too, so kept references to all pending messages in a database, which I would frequently scan for postponed messages. A message would get postponed if there is no radio, or the sending simply fails for whatever reason.

我还将 GCM 与 SMS 结合使用,以尽可能快地传递消息,同时使用两个渠道发送消息.

I also used GCM together with SMS to get the message delivered as fast as possible, sending messages using both channels at the same time.

Edit2:哦,还不如解决问题,反正我们差不多了:

Oh well, might as well address the questions, we are almost there anyway:

问题 1: 由于使用 IntentService,发送是在后台完成的.

Question 1: Since using IntentService the sending is done in the background.

您只希望在延迟后发送一次,因此您应该这样做:

You only want the sending to happen once after a delay so you should do this instead:

    new Handler().postDelayed(new Runnable() {

        @Override
        public void run() {
            // send sms
        }
    }, delay);

问题 2: 很简单,当您发送的消息广播检测到错误时,请执行上述方法.除了接收者和消息之外,您还可以添加额外的信息,计算到目前为止的重试次数,以便您有机会停止发送/重试循环.

Question 2: Easy, when your sent message broadcast detects an error do the above method. You could add an extra information, besides receiver and message, counting the number of retries up until now so you have a chance of stopping the send/retry loop.

问题 3: 发送会自行停止,因为它是一项意图服务.至于其他服务,我认为最简单的方法是发送一个公共广播,由您的主要活动接收.这样您就可以在正确的位置获取服务并停止它.

Question 3: The sending stops by itself, as it is an Intent Service. As for the other service the most simple approach, I think, would be to send a common broadcast, which is picked up by your main activity. This way you can get a hold of the service the right place and stop it.

这篇关于发送短信直到成功的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆