尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Expo SMS 模块指南:用 expo-sms 调用系统短信能力发送 SMS 消息

Expo SMS 模块指南:用 expo-sms 调用系统短信能力发送 SMS 消息 Expo SMS 模块指南用 expo-sms 调用系统短信能力发送 SMS 消息【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo导读expo-sms是 Expo SDK 中负责短信能力的官方模块它不直接替应用发送短信而是通过系统自带的短信 UI/应用让用户完成发送动作从而规避了 SMS 发送权限的合规风险。本文将基于 packages/expo-sms 的源码与 packages/expo-sms/README.md完整讲解其安装步骤、SMS.sendSMSAsync与SMS.isAvailableAsync两个核心 API、附件配置、Android 与 iOS 的平台差异以及底层 Intent 与MFMessageComposeViewController的实现原理帮助你准确判断“设备是否有短信能力”并在不同平台上稳健地唤起短信编辑界面。模块定位唤起系统短信界面而非直发短信模块的定位可以从 packages/expo-sms/package.json 中的描述看到Provides access to the systems UI/app for sending SMS messages.即它“提供对系统发送 SMS 消息的 UI/应用 的访问”。这意味着应用只负责预填收件人、正文和附件然后唤起系统短信编辑器真正发送动作由用户手动触发模块不会在后台静默发送短信也不需要SEND_SMS权限模块能反馈的结果是用户是否触发了发送而不是短信是否真正送达——正如SMS.ts源码中的注释所言The only feedback collected by this module is whether any message has been sent. That means we do not check actual content of message nor recipients list.见 packages/expo-sms/src/SMS.ts。支持平台为 Android、iOS 及 Expo Go模块版本当前为57.0.1。安装与配置托管managedExpo 项目对于托管工作流项目直接使用 Expo CLI 安装即可它会自动选择与当前 SDK 匹配的版本npx expo install expo-sms裸bareReact Native 项目在裸工程中需要先确保已经安装并配置好expo包expo-modules-core等基础设施再执行同样的安装命令npx expo install expo-sms平台配置差异Android无需任何额外配置。源码中的 packages/expo-sms/android/src/main/AndroidManifest.xml 仅声明了queries清单用于在 Android 11API 30及以上系统查询短信应用的可达性包括ACTION_SEND文件分享与ACTION_SENDTOsms/smstoscheme两类 Intent不需要申请任何权限。iOS安装 npm 包之后运行一次 CocoaPods 安装npx pod-installnpx pod-install是 Expo 提供的便捷命令等价于先pod install再同步更新Podfile.lock。核心 API 详解模块的 JavaScript 侧入口为 packages/expo-sms/src/SMS.ts它通过 packages/expo-sms/src/ExpoSMS.native.ts 中的requireNativeModule(ExpoSMS)调用原生模块。共有两个导出方法。SMS.isAvailableAsync()const isAvailable await SMS.isAvailableAsync(); if (isAvailable) { // 设备支持发送短信 } else { // 设备不支持短信例如 iPad、模拟器等 }返回Promiseboolean指示当前设备是否有可用的短信服务。iOS 模拟器与浏览器web中恒为falseiOS 侧实现调用MFMessageComposeViewController.canSendText()见 packages/expo-sms/ios/ExpoSMSModule.swift模拟器通常无法发送短信web 平台则没有原生实现直接返回false。Android 侧实现通过PackageManager.hasSystemFeature(FEATURE_TELEPHONY)判断设备是否具备电话/短信硬件能力见 packages/expo-sms/android/src/main/java/expo/modules/sms/SMSModule.kt因此平板等无短信能力的设备会返回false。这是一个推荐在调用sendSMSAsync之前先执行的“能力探测”方法避免在无短信能力的设备上唤起失败。SMS.sendSMSAsync(addresses, message, options)import * as SMS from expo-sms; const { result } await SMS.sendSMSAsync( [0123456789, 9876543210], My sample HelloWorld message, { attachments: { uri: path/myfile.png, mimeType: image/png, filename: myfile.png, }, } );参数说明参数类型说明addressesstring \| string[]收件人电话号码可以传单个字符串或字符串数组。内部会统一归一化为数组见下方测试与源码说明。messagestring预填到短信编辑器中的正文。optionsSMSOptions可选附加配置目前仅支持attachments附件。返回值SMSResponsetype SMSResponse { result: unknown | sent | cancelled; };取值含义cancelled用户取消了短信发送流程。sent用户已发送或已调度短信。unknown无法确定短信的最终状态。需要特别注意的是Android 上该 Promise 恒以{ result: unknown }解决。原因是确定短信真实状态需要读取设备短信数据库READ_SMS权限而 Google 自 2019 年起严格限制该权限的申请因此模块选择不做状态查询只返回unknown见 packages/expo-sms/android/src/main/java/expo/modules/sms/SMSModule.kt 中onHostResume的实现注释。参数校验与归一化源码在 packages/expo-sms/src/SMS.ts 中做了三件事单个地址字符串会被包装为数组Array.isArray(addresses) ? addresses : [addresses]对每个地址做空值检查null或undefined会抛出TypeError(undefined or null address)附件会被归一化为数组processAttachments会把单个附件包装为数组在 Android 上如果传了多个附件只会保留第一个并在开发模式下打印警告Android only supports a single attachment. The first array item is used.。这一行为由单元测试直接覆盖验证见 packages/expo-sms/src/tests/SMS-test.ts测试确认0123456789会被归一化为[0123456789]传给原生层且 Android 上多个附件会被裁剪为仅第一项。附件类型SMSAttachmenttype SMSAttachment { uri: string; // 附件的内容 URI必须是 content URI才能被短信应用等外部应用访问 mimeType: string; // 附件 MIME 类型如 image/png filename: string; // 附件文件名 };类型定义见 packages/expo-sms/src/SMS.types.ts。uri必须是content URI这样系统短信应用才能在 Expo 应用之外读取该文件如需将本地文件转换为 content URI可参考expo-file-system的FileSystem.getContentUriAsync。Android 与 iOS 的原生侧分别通过 packages/expo-sms/android/src/main/java/expo/modules/sms/SMSOptions.kt 与 packages/expo-sms/ios/SMSOptions.swift 中的Record结构接收这三个字段。平台底层实现原理AndroidIntent 唤起系统短信应用Android 实现在 packages/expo-sms/android/src/main/java/expo/modules/sms/SMSModule.kt 中核心逻辑是构造 Intent无附件时使用Intent.ACTION_SENDTOdata 为smsto: 用分号连接的收件人列表这是最标准的唤起短信编辑器方式有附件时使用Intent.ACTION_SEND因为只有该 Intent 支持携带附件先设type text/plain并写入addressextra再设置EXTRA_STREAM附件 URI与真实的 MIME 类型并添加FLAG_GRANT_READ_URI_PERMISSION授予接收方读取权限。源码注释特别提到ACTION_SEND在 Android 10 上若短信应用未在后台打开会出现轻微闪烁但它似乎是唯一支持附件的 Intent因此有附件时只能用它两类 Intent 都会额外写入三个 extraexit_on_sent、compose_mode、sms_body消息正文随后通过Telephony.Sms.getDefaultSmsPackage(context)获取系统默认短信应用包名并setPackage若设备上没有可用的短信应用会抛出MissingSMSAppException定义于 packages/expo-sms/android/src/main/java/expo/modules/sms/SMSExceptions.kt。模块还实现了LifecycleEventListener在唤起短信界面onHostPause后当宿主 Activity 恢复onHostResume时用保存的pendingPromise以{ result: unknown }解决 Promise 并清空状态。这就是 Android 恒返回unknown的机制来源。iOSMFMessageComposeViewController呈现系统短信面板iOS 实现在 packages/expo-sms/ios/ExpoSMSModule.swift 中先调用MFMessageComposeViewController.canSendText()检查能力不可用则抛出SMSUnavailableException检查是否已有进行中的短信流程smsContext ! nil若有则抛出SMSPendingException即 iOS 上同一时间只允许一个待处理的短信请求创建MFMessageComposeViewController设置recipients收件人数组与body正文遍历附件先用UTTypeCreatePreferredIdentifierForTag将 MIME 类型转换为 UTI转换失败抛出SMSMimeTypeException再校验URL(string: uri)合法性SMSUriException读取文件数据并调用addAttachmentData(_:typeIdentifier:filename:)添加附件文件读取或附加失败会拒绝 Promise通过utils?.currentViewController()找到当前控制器并以模态方式present系统短信界面。用户操作的结果由 packages/expo-sms/ios/SMSDelegate.swift 中的SMSDelegate处理它实现了MFMessageComposeViewControllerDelegate.sent→ 回调{ result: sent }.cancelled→ 回调{ result: cancelled }.failed如设备断网导致发送失败→ 以SMSSendingException拒绝 Promise其他未知结果 → 以通用失败信息拒绝 Promise。错误与异常一览平台异常触发条件AndroidMissingSMSAppException设备上没有可用的短信应用无法解析默认短信包名iOSSMSUnavailableException设备不支持短信如 iPad、模拟器iOSSMSPendingException已有短信流程未结束重复调用sendSMSAsynciOSSMSMimeTypeException无法根据 MIME 类型找到对应 UTIiOSSMSUriException附件uri不是合法 URLiOSSMSFileException附件文件数据读取或附加失败iOSSMSSendingException用户发送失败如失去网络连接通用UnavailabilityError(expo-sms, sendSMSAsync)平台无原生实现如 web时调用 API 抛出使用建议与平台注意点综合源码实现实际接入时建议遵循以下实践先探测、再唤起调用sendSMSAsync前先await SMS.isAvailableAsync()尤其是 iPad、Android 平板、iOS 模拟器这类可能无短信能力的设备预期 Android 的unknown不要用 Android 的返回结果判断短信是否真正发出它只代表“短信编辑器已退出”iOS 的sent/cancelled才反映用户的明确动作Android 单附件限制多附件需求仅 iOS 支持Android 上只会附加第一项应在业务层按平台裁剪附件列表避免开发警告与用户困惑附件必须使用 content URI普通文件路径无法被短信应用跨进程读取请先通过expo-file-system的getContentUriAsync转换iOS 并发限制同一时间只允许一个进行中的短信流程重复调用会收到SMSPendingException业务上可加互斥或等待前一个请求完成。结语expo-sms以“唤起系统短信界面”的方式绕开了繁琐且受限的短信发送权限让开发者用极少的代码实现“预填收件人 正文 附件”的短信体验。理解其 Android Intent 与 iOSMFMessageComposeViewController两套底层实现、把握两平台在返回结果与附件能力上的差异就能在真实的跨平台业务中写出稳健的短信功能。如需继续深入可直接阅读本仓库中的 SMS.tsAPI 层、SMSModule.ktAndroid 实现、ExpoSMSModule.swiftiOS 实现以及 SMS-test.ts行为测试。【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表