android - 短信发送:系统提示短信发送成功,但实际上没有
问题描述
我的应用通过 SmsManager.sendMultipartTextMessage 发送短信,然后通知服务器短信发送的状态。
一切正常,但 Yota 运算符有问题。短信不发送到 MTS 运营商的手机。Yota 运营商的员工声称 MTS 运营商阻止接收来自 Yota 的消息。
这不是我们的问题,但是安卓系统说这样的短信发送成功了。我们无法从另一部手机重新发送此类短信,因为我们的系统认为它们已经成功发送。
如果应用程序尝试发送此类 SMS,系统首先会发出成功发送 SMS 的信号,然后发出成功发送 SMS 的信号。听起来不错,但实际上短信没有送达。这不是一个孤立的案例。在不同的设备上进行了测试。还尝试使用 MTS 运营商向不同的手机发送短信。
我尝试通过标准的“消息”应用程序发送相同的短信并注意到以下内容:
从我的应用程序发送的 SMS 在不同设备上的标准“消息”应用程序中的显示方式不同。例如,ZTE BLADE L110 (API 22) 显示发送消息时出错,而小米红米 3S (API 23) 则没有。但是,当我尝试通过标准应用程序发送此类短信时,在这两款智能手机上都会显示错误发送消息(中兴通讯在此之前显示 Toast“短信成功发送”)。
谁能帮忙?是否有其他方法可以检查 SMS 是否已发送?标准“消息”应用程序如何在系统生成带有消息已成功发送和传递的信号的意图时记录 SMS 发送错误的事实?
这是我的代码:
发送短信作业:
public class SendSMSJob extends SimpleJobService {
public static final String EXTRA_SMS_LIST_JSON = "sms_list";
public static final String TAG = "send_sms_job";
private static final String ACTION_SENT = "ru.sp2all.smsgate.SMS_SENT";
private static final String ACTION_DELIVERED = "ru.sp2all.smsgate.SMS_DELIVERED";
private int startId;
@Override
public void onCreate() {
super.onCreate();
MyLog.i(getLogTag(), "onCreate()");
}
@Override
public int onRunJob(JobParameters parameters) {
try {
this.startId = startId;
String json = parameters.getExtras().getString(EXTRA_SMS_LIST_JSON);
Gson gson = new Gson();
SMSDataList smsDataList = gson.fromJson(json, SMSDataList.class);
MyLog.d(getLogTag(), "Sending " + String.valueOf(smsDataList.items.length) + " messages");
boolean complete = true;
boolean needRetry = true;
for (SMSData sms: smsDataList.items) {
Integer subscriptionId = SimsHelper.getSimSubscriptionIdForIndex(getApplicationContext(), sms.simIndex);
SmsManager smsManager = null;
if (subscriptionId != null) {
smsManager = SmsManager.getSmsManagerForSubscriptionId(subscriptionId);
} else {
smsManager = SmsManager.getDefault();
}
ArrayList<String> parts = smsManager.divideMessage(sms.message);
MyLog.i(getLogTag(), "parts.size=" + parts.size());
insertSmsToDB(sms, parts);
ArrayList<PendingIntent> deliveryIntents = new ArrayList<>(parts.size());
ArrayList<PendingIntent> sentIntents = new ArrayList<>(parts.size());
try {
for (int part = 0; part < parts.size(); part++) {
Intent sentIntent = new Intent(ACTION_SENT);
sentIntent.putExtra("id", sms.smsId + "_" + Integer.toString(part));
sentIntent.putExtra("sms_id", sms.smsId);
sentIntent.putExtra("parts", Integer.toString(parts.size()));
sentIntent.putExtra("part", Integer.toString(part));
sentIntent.putExtra("phone", sms.phone);
sentIntent.putExtra("msg", sms.message);
PendingIntent sentPI = PendingIntent.getBroadcast(this,
Integer.parseInt(sms.smsId) * 100 + part,
sentIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
sentIntents.add(sentPI);
Intent deliveredIntent = new Intent(ACTION_DELIVERED);
deliveredIntent.putExtra("id", sms.smsId + "_" + Integer.toString(part));
deliveredIntent.putExtra("sms_id", sms.smsId);
deliveredIntent.putExtra("parts", Integer.toString(parts.size()));
deliveredIntent.putExtra("part", Integer.toString(part));
deliveredIntent.putExtra("phone", sms.phone);
deliveredIntent.putExtra("msg", sms.message);
PendingIntent deliveredPI = PendingIntent.getBroadcast(this,
Integer.parseInt(sms.smsId) * 100 + part,
deliveredIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
deliveryIntents.add(deliveredPI);
}
MyLog.i(getLogTag(), "Sending to " + sms.phone);
smsManager.sendMultipartTextMessage(sms.phone, null, parts,
sentIntents, deliveryIntents);
} catch (Exception e) {
ErrorReporter.exception(this, getLogTag(), e, new ErrorReporter.ReporterHandler(e));
complete = true;
}
}
if (complete) {
return RESULT_SUCCESS;
} else if (needRetry) {
return RESULT_FAIL_RETRY;
} else {
return RESULT_FAIL_NORETRY;
}
} catch (Exception e) {
ErrorReporter.exception(this, getLogTag(), e, new ErrorReporter.ReporterHandler(e));
return RESULT_FAIL_RETRY;
}
}
private String getLogTag() {
return getClass().getSimpleName();
}
}
在 AndroidManifest 广播接收器中注册用于 SMS 状态处理:
<receiver android:name=".SMSSentReceiver">
<intent-filter>
<action android:name="ru.sp2all.smsgate.SMS_SENT" />
</intent-filter>
</receiver>
<receiver android:name=".SMSDeliveredReceiver">
<intent-filter>
<action android:name="ru.sp2all.smsgate.SMS_DELIVERED" />
</intent-filter>
</receiver>
短信发送广播接收器:
public class SMSSentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
try {
String sms_id = intent.getStringExtra("sms_id");
int part = Integer.parseInt(intent.getStringExtra("part"));
MyLog.d(getLogTag(), "br_sent " + sms_id);
int status = 0;
int code = getResultCode();
switch (code) {
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
status = 400;
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
status = 401;
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
status = 402;
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
status = 403;
break;
case Activity.RESULT_OK:
updateDBWithNewSentPartsCount();
status = 202; // 202 is SMS sent Server status
break;
default:
status = 404; break;
}
if (status == 202) {
int sent = getSentPartsFromDB();
int parts = getAllPartsFromDB();
MyLog.d(getLogTag(), "DB sent:" + sms_id + " - : sent:" + sent + " parts: " + parts);
if (sent >= parts) {
MyLog.d(getLogTag(), "DB sent all parts");
Network.sendSMSStatus(context, sms_id, status);
}
} else {
Network.sendSMSStatus(context, sms_id, status);
}
} catch (Exception e) {
ErrorReporter.exception(context, getLogTag(), e, new ErrorReporter.ReporterHandler(e));
}
}
private String getLogTag() {
return getClass().getSimpleName();
}
}
短信发送广播接收器:
public class SMSDeliveredReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
try {
String sms_id = intent.getStringExtra("sms_id");
int part = Integer.parseInt(intent.getStringExtra("part"));
MyLog.d(getLogTag(), "br_delivered " + sms_id);
int resultCode = getResultCode();
switch (resultCode) {
case Activity.RESULT_OK:
updateDBWithNewDeliveredPartsCount();
int delivered = getDeliveredPartsFromDB();
int parts = getAllPartsFromDB();
MyLog.i(getLogTag(), "DB delivered: " + sms_id + ", " + delivered + " parts of " + parts);
if (delivered >= parts) {
MyLog.i(getLogTag(), "DB delivered all parts");
Network.sendSMSStatus(context, sms_id, 200); // 200 is SMS delivered Server status
}
break;
case Activity.RESULT_CANCELED:
MyLog.w(getLogTag(), "DB delivered: CANCELLED " + sms_id);
Network.sendSMSStatus(context, sms_id, 405);
break;
default:
MyLog.w(getLogTag(), "DB delivered: unknown code " + resultCode);
Network.sendSMSStatus(context, sms_id, resultCode);
}
} catch (Exception e) {
ErrorReporter.exception(context, getLogTag(), e, new ErrorReporter.ReporterHandler(e));
}
}
private String getLogTag() {
return getClass().getSimpleName();
}
}
当我尝试发送 SMS 和 SMS 未实际发送时从设备登录:
SendSMSJob: onCreate()
SendSMSJob: Sending 1 messages
SendSMSJob: parts.size=1
SendSMSJob: Sending to +7988*******
SMSSentReceiver: br_sent 704402
SMSSentReceiver: DB sent:704402 - : sent:1 parts: 1
SMSSentReceiver: DB sent all parts
Network: sendSMSStatus status: 202
Network: get: https://{SERVER_NAME}/result.php?status=202&smsId=704402
Network: got: {"status":200}
SMSDeliveredReceiver: br_delivered 704402
SMSDeliveredReceiver: DB delivered: 704402, 1 parts of 1
SMSDeliveredReceiver: DB delivered all parts
Network: sendSMSStatus status: 200
Network: get: https://{SERVER_NAME}/result.php?status=200&smsId=704402
Network: got: {"status":200}
测试短信:gate4
解决方案
SMS 不是公认的协议。您无法知道它是否已从发送端交付。发件人只能知道消息何时发送。发送正确报告,因为正在发送数据,但在另一端被阻止。
如果您需要确认短信,您可以从接收方回复,但这将涉及开发您自己的确认协议。
我只是从设备轮询网络服务。无论如何,数据比短信便宜很多。
祝你好运。
推荐阅读
- bash - 重命名多个文件,UPPER 为 lower 和 lower 为 UPPER
- python - Python MYSQL 插入一个值
- php - 制作 PHP 下拉框从 SQL 数据库中提取数据
- r - 如何在无序字符串之间找到匹配项
- python - 如何将登录用户的用户名添加到 django 中的模型字段
- c++ - 双向链表上的 C++ 冒泡排序
- c++ - C ++中类的私有成员内的数组初始化
- reactjs - 反应空 useEffect
- javascript - 使用 ajax 将 JSON 数组转换为选择标签仅获得 1 个值
- python - 对 Python 很陌生,如何为这个测试编写代码?