You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi Firemonkey Android8下短信发送状态检测及系统短信应用显示问题

问题原因说明

从Android 4.4开始,只有系统默认短信应用调用SmsManager发送的短信,才会被系统自动写入短信公共Provider(content://sms/*路径)。非默认短信的第三方应用直接调用API发送的短信,系统不会自动存储,所以你既没法查到记录,系统默认短信应用也看不到。

问题1解决方案:让应用发出的短信显示在默认短信应用中

有两种实现方案,可根据业务需求选择:

方案1:手动将短信记录插入系统短信Provider(支持后台自动发送)

是不需要用户交互、实现后台自动发短信的首选方案,操作步骤如下:

  • 首先申请必要权限:除了已有的SEND_SMS权限,还需要动态申请READ_SMS和WRITE_SMS权限(Android 6.0及以上不能只在AndroidManifest.xml里声明,必须运行时弹窗申请用户授权)
  • 发送短信后立即向系统短信Provider插入记录,插入示例逻辑(Delphi语法):
var
  values: JContentValues;
begin
  values := TJContentValues.JavaClass.init;
  values.put(StringToJString('address'), StringToJString(targetstr)); // 收信号码
  values.put(StringToJString('body'), StringToJString(bodystr)); // 短信内容
  values.put(StringToJString('type'), Integer.valueOf(2)); // 2对应TYPE_SENT已发送类型,后续发送失败可以更新为3(TYPE_FAILED)
  values.put(StringToJString('status'), Integer.valueOf(0)); // 0对应STATUS_PENDING待确认发送结果
  values.put(StringToJString('date'), Integer.valueOf(JavaSystem.currentTimeMillis)); // 发送时间戳
  TAndroidHelper.Activity.getContentResolver.insert(StrToJURI('content://sms/sent'), values);
end;
  • 后续确认短信发送失败后,更新对应记录的type为3,或者直接移动到content://sms/failed路径下即可。

方案2:调用系统短信应用发送(无需申请短信权限,需要用户交互)

如果业务允许用户手动确认发送,可以直接调起系统默认短信应用发送,短信会自动由系统应用存入公共Provider,无需手动操作:

var
  intent: JIntent;
  uri: Jnet_Uri;
begin
  uri := TJnet_Uri.JavaClass.parse(StringToJString('smsto:' + targetstr));
  intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_SENDTO, uri);
  intent.putExtra(StringToJString('sms_body'), StringToJString(bodystr));
  TAndroidHelper.Activity.startActivity(intent);
end;

缺点是会跳转到系统短信的发送界面,需要用户手动点击发送按钮,不能实现后台自动发送和自动重发逻辑。

问题2解决方案:正确判断短信发送和送达状态

你之前调用sendMultipartTextMessage时后两个参数传了nil,这两个参数就是用来监听发送状态和送达状态的PendingIntent,按以下步骤实现即可:

  1. 注册两个广播接收器,分别监听短信发送结果和对方送达回执:
  • 发送结果广播:可以判断短信是否成功从本机发出(信号差、欠费等原因发不出会返回失败)
  • 送达回执广播:可以判断对方是否成功收到短信(需要运营商支持,国内大部分运营商默认支持)
  1. 发送短信时构造对应的PendingIntent数组,传入sendMultipartTextMessage方法:
// 示例:构造发送状态的PendingIntent,每个拆分后的短信段对应一个
var
  sentIntents: TJArrayList;
  sentPI: JPendingIntent;
  i: Integer;
begin
  sentIntents := TJArrayList.JavaClass.init;
  for i := 0 to smsarray.size - 1 do
  begin
    intent := TJIntent.JavaClass.init(StringToJString('com.你的包名.SMS_SENT'));
    // 传入短信标识,方便收到广播时匹配对应的短信
    intent.putExtra(StringToJString('sms_id'), 你生成的短信唯一ID);
    sentPI := TJPendingIntent.JavaClass.getBroadcast(TAndroidHelper.Activity, i, intent, 0);
    sentIntents.add(sentPI);
  end;
  // 送达回执的PendingIntent构造逻辑同上,action自定义为比如com.你的包名.SMS_DELIVERED
  // 调用发送接口时传入
  smsManager.sendmultiparttextMessage(smsTo, nil, smsarray, sentIntents, 送达PendingIntent数组);
end;
  1. 在广播接收器的onReceive方法里处理结果:
  • 发送结果广播:getResultCode返回Activity.RESULT_OK就是发送成功,返回SmsManager.RESULT_ERROR_GENERIC_FAILURE、RESULT_ERROR_NO_SERVICE等常量就是发送失败,你可以根据错误类型触发自动重发逻辑。
  • 送达回执广播:getResultCode返回Activity.RESULT_OK就是对方已成功接收。
注意事项
  • Android 8.0及以上对静态注册的隐式广播有限制,建议动态注册广播接收器,避免收不到回调。
  • 如果使用手动插入短信记录的方案,收到发送状态回调后记得同步更新系统Provider里的短信状态,这样默认短信应用也能显示正确的发送结果。

内容的提问来源于stack exchange,提问作者jwan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 03:36:03