9

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

在 Android 系统启动并进行检查后发送 SMS。

所以我有一个处理 BOOT_COMPLETED 意图过滤器的服务类。此类进行检查,如果某些情况属实,则它会尝试通过另一个“扩展服务”的类发送 SMS 消息

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

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

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秒重试一次?

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

这是我的代码不起作用:

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 似乎从未运行。

- 编辑 -

所以,这是我最后的工作代码。我想我的情况很特殊,因为它不适用于 UI 线程。我有一个在 Boot 上运行的广播接收器。我正在尝试发送 SMS 消息,直到成功发送。

此引导广播接收器启动服务。这是其中的一些代码:

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 类如下:

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 类的简化(仅接受一个接收器)版本如下:

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

1 回答 1

14

这是我所做的:

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

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

在您的清单中记住:

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

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

或者,您可以在发送和/或传递消息时收听:

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

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

编辑:添加了我的 messageSent 和 messageDelivered 实现

这些是特定于我的实现的,因此包含一些您无法使用的代码,仅用于演示。

已发送消息:

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

}

我也需要可靠的发送,因此将所有未决消息的引用保存在数据库中,我会经常扫描这些消息以查找延迟消息。如果没有无线电,消息将被推迟,或者发送由于任何原因而失败。

我还结合使用 GCM 和 SMS 来尽可能快地传递消息,同时使用两个通道发送消息。

Edit2:哦,好吧,不妨解决问题,反正我们几乎就在那里:

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

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

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

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

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

问题 3:发送自行停止,因为它是 Intent Service。至于其他服务,我认为最简单的方法是发送一个公共广播,由您的主要活动接收。通过这种方式,您可以在正确的位置获取服务并停止它。

于 2013-09-29T23:40:25.343 回答