React Native中SmsManager.sendMultimediaMessage使用及contentUri创建问题
解决Android SmsManager.sendMultimediaMessage()发送MMS失败的问题
你的代码存在几个关键问题导致MMS发送失败,以下是修复方案:
1. 补充MMS核心必填字段
Telephony.Mms表需要一些必填字段才能让系统识别为可发送的MMS,你的代码只设置了SUBJECT,缺失了日期、消息类型、状态等关键字段:
ContentValues mmsValues = new ContentValues(); mmsValues.put(Telephony.Mms.SUBJECT, "Subject"); // 补充必填字段 mmsValues.put(Telephony.Mms.DATE, System.currentTimeMillis() / 1000); // Unix时间戳(秒) mmsValues.put(Telephony.Mms.MESSAGE_TYPE, Telephony.Mms.MESSAGE_TYPE_SEND); // 标记为待发送的消息 mmsValues.put(Telephony.Mms.READ, 1); // 标记为已读(避免系统重复处理) mmsValues.put(Telephony.Mms.STATUS, Telephony.Mms.STATUS_PENDING); // 标记为待发送状态 mmsValues.put(Telephony.Mms.REPORT_REQUESTED, 0); // 关闭送达报告(可选)
2. 修正收件人地址类型
你使用的TYPE=137不是标准的收件人类型,应该使用官方定义的TYPE_TO:
ContentValues addrValues = new ContentValues(); addrValues.put(Telephony.Mms.Addr.MSG_ID, messageId); addrValues.put(Telephony.Mms.Addr.ADDRESS, phoneNumber); addrValues.put(Telephony.Mms.Addr.TYPE, Telephony.Mms.Addr.TYPE_TO); // 标准收件人类型(值为1) addrValues.put(Telephony.Mms.Addr.CHARSET, "UTF-8"); // 补充字符集
3. 正确存储文本内容
Telephony.Mms.Part的TEXT字段并非所有系统都支持,正确的做法是将文本写入到ContentResolver的输出流中:
Uri partUri = Uri.withAppendedPath(mmsUri, "part"); ContentValues partValues = new ContentValues(); partValues.put(Telephony.Mms.Part.MSG_ID, messageId); partValues.put(Telephony.Mms.Part.CONTENT_TYPE, "text/plain"); partValues.put(Telephony.Mms.Part.NAME, "body.txt"); partValues.put(Telephony.Mms.Part.FILENAME, "body.txt"); partValues.put(Telephony.Mms.Part.CONTENT_LOCATION, "body.txt"); partValues.put(Telephony.Mms.Part.CHARSET, "UTF-8"); Uri insertedPartUri = context.getContentResolver().insert(partUri, partValues); if (insertedPartUri == null) { Log.e("SendDirectSms", "Failed to create text part"); context.getContentResolver().delete(mmsUri, null, null); // 清理无效MMS return; } // 将文本写入到part的输出流 try (OutputStream os = context.getContentResolver().openOutputStream(insertedPartUri)) { os.write(msg.getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { Log.e("SendDirectSms", "Failed to write text content", e); context.getContentResolver().delete(mmsUri, null, null); return; }
4. 添加发送状态监听(可选但关键)
通过PendingIntent监听发送结果,既能确认是否发送成功,也能在失败时清理无效记录:
// 创建发送状态广播的PendingIntent String SENT_ACTION = "com.your.app.package.MMS_SENT"; Intent sentIntent = new Intent(SENT_ACTION); PendingIntent sentPendingIntent = PendingIntent.getBroadcast( context, 0, sentIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); // 发送MMS smsManager.sendMultimediaMessage(context, mmsUri, null, null, sentPendingIntent);
然后注册广播接收器处理结果(在React Native模块中初始化时注册):
private BroadcastReceiver mmsSentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { int resultCode = getResultCode(); switch (resultCode) { case Activity.RESULT_OK: Log.i("SendDirectSms", "MMS发送成功"); // 更新MMS状态为已完成 ContentValues updateValues = new ContentValues(); updateValues.put(Telephony.Mms.STATUS, Telephony.Mms.STATUS_COMPLETE); context.getContentResolver().update(mmsUri, updateValues, null, null); 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: Log.e("SendDirectSms", "MMS发送失败,错误码:" + resultCode); // 更新MMS状态为失败 ContentValues failValues = new ContentValues(); failValues.put(Telephony.Mms.STATUS, Telephony.Mms.STATUS_FAILED); context.getContentResolver().update(mmsUri, failValues, null, null); break; } } }; // 在模块初始化时注册接收器 @Override public void initialize(ReactApplicationContext reactContext) { super.initialize(reactContext); this.context = reactContext; IntentFilter filter = new IntentFilter(SENT_ACTION); reactContext.registerReceiver(mmsSentReceiver, filter); } // 在模块销毁时注销 @Override public void onCatalystInstanceDestroy() { super.onCatalystInstanceDestroy(); if (mmsSentReceiver != null) { context.unregisterReceiver(mmsSentReceiver); } }
5. 确保权限齐全
在AndroidManifest.xml中添加必要权限,并在运行时动态申请:
<uses-permission android:name="android.permission.SEND_MMS" /> <uses-permission android:name="android.permission.WRITE_SMS" /> <uses-permission android:name="android.permission.READ_SMS" /> <!-- Android 12+ 需要通知权限才能触发广播 --> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
最终修正后的完整代码
@ReactMethod public void sendDirectSms(String phoneNumber, String msg) { // 检查权限(需要提前动态申请) if (ContextCompat.checkSelfPermission(context, Manifest.permission.SEND_MMS) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_SMS) != PackageManager.PERMISSION_GRANTED) { Log.e("SendDirectSms", "Missing required permissions"); return; } try { SmsManager smsManager = SmsManager.getDefault(); // 1. 创建MMS主记录 ContentValues mmsValues = new ContentValues(); mmsValues.put(Telephony.Mms.SUBJECT, "Subject"); mmsValues.put(Telephony.Mms.DATE, System.currentTimeMillis() / 1000); mmsValues.put(Telephony.Mms.MESSAGE_TYPE, Telephony.Mms.MESSAGE_TYPE_SEND); mmsValues.put(Telephony.Mms.READ, 1); mmsValues.put(Telephony.Mms.STATUS, Telephony.Mms.STATUS_PENDING); mmsValues.put(Telephony.Mms.REPORT_REQUESTED, 0); Uri mmsUri = context.getContentResolver().insert(Telephony.Mms.CONTENT_URI, mmsValues); if (mmsUri == null) { Log.e("SendDirectSms", "Failed to create MMS message."); return; } String messageId = mmsUri.getLastPathSegment(); // 2. 添加收件人地址 Uri addrUri = Uri.withAppendedPath(mmsUri, "addr"); ContentValues addrValues = new ContentValues(); addrValues.put(Telephony.Mms.Addr.MSG_ID, messageId); addrValues.put(Telephony.Mms.Addr.ADDRESS, phoneNumber); addrValues.put(Telephony.Mms.Addr.TYPE, Telephony.Mms.Addr.TYPE_TO); addrValues.put(Telephony.Mms.Addr.CHARSET, "UTF-8"); Uri insertedAddrUri = context.getContentResolver().insert(addrUri, addrValues); if (insertedAddrUri == null) { Log.e("SendDirectSms", "Failed to add recipient"); context.getContentResolver().delete(mmsUri, null, null); return; } // 3. 添加文本内容部分 Uri partUri = Uri.withAppendedPath(mmsUri, "part"); ContentValues partValues = new ContentValues(); partValues.put(Telephony.Mms.Part.MSG_ID, messageId); partValues.put(Telephony.Mms.Part.CONTENT_TYPE, "text/plain"); partValues.put(Telephony.Mms.Part.NAME, "body.txt"); partValues.put(Telephony.Mms.Part.FILENAME, "body.txt"); partValues.put(Telephony.Mms.Part.CONTENT_LOCATION, "body.txt"); partValues.put(Telephony.Mms.Part.CHARSET, "UTF-8"); Uri insertedPartUri = context.getContentResolver().insert(partUri, partValues); if (insertedPartUri == null) { Log.e("SendDirectSms", "Failed to create text part"); context.getContentResolver().delete(mmsUri, null, null); return; } try (OutputStream os = context.getContentResolver().openOutputStream(insertedPartUri)) { os.write(msg.getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { Log.e("SendDirectSms", "Failed to write text content", e); context.getContentResolver().delete(mmsUri, null, null); return; } // 4. 发送MMS并监听状态 String SENT_ACTION = "com.your.app.package.MMS_SENT"; Intent sentIntent = new Intent(SENT_ACTION); PendingIntent sentPendingIntent = PendingIntent.getBroadcast( context, 0, sentIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); smsManager.sendMultimediaMessage(context, mmsUri, null, null, sentPendingIntent); } catch (Exception ex) { Log.e("SendDirectSms", "Exception occurred", ex); } }
注意事项
- 部分运营商要求MMS必须包含主题(SUBJECT),即使是空字符串也可以,但不能缺失
- Android 10及以上对短信/MMS的读写权限限制更严格,确保动态申请权限时用户授权
- 若仍发送失败,可检查设备的MMSC配置是否正确(可通过
Telephony.Carriers获取默认配置,或在sendMultimediaMessage的第三个参数configOverrides中指定)
内容的提问来源于stack exchange,提问作者이제준
相关产品推荐
相关产品推荐

