三大發送短信架構對比
| 方式 | 原理 | 優點 | 缺點 | 適用場景 |
| 伺服器網關 | App → HTTP → 運營商 SMPP/HTTP 閘道 → 手機 | 成本可控、支援長短信、狀態報告、國際發送 | 需伺服器開發/租用、網絡延遲 | 商業營銷、驗證碼、通知系統 |
| PC 數據線 + AT 指令 | PC 程式 → USB/藍牙 → 手機 Modem → AT+CMGS | 免費(含套餐)、直接用手機卡 | 需實體連接、阻塞手機、跨平台難 | 個人工具、測試、小規模發送 |
| 手機端 J2ME (WMA) | MIDlet → WMA API → 手機基帶 → 基站 | 無伺服器、用戶感知透明、可整合遊戲邏輯 | 受手機權限限制、可能彈出確認框、計費不可控 | 遊戲邀請、分享分數、社交功能 |
WMA (Wireless Messaging API) 架構
- JSR 120 (WMA 1.0):MIDP 1.0 選配,支援 SMS 文本/二進制、CBS (小區廣播)。
- JSR 205 (WMA 2.0):MIDP 2.0 內建,新增 MMS、多段短信、狀態報告監聽。
- 核心類:
javax.wireless.messaging.MessageConnection, TextMessage, BinaryMessage。
標準發送流程
import javax.wireless.messaging.*;
import javax.microedition.io.*;
public class SmsSender {
// 發送文本短信
// @return true 發送請求已提交給基帶 (不保證對方收到)
public static boolean sendText(String phoneNumber, String content) {
MessageConnection conn = null;
try {
// 1. 目標地址: sms://+國碼號碼 (國際格式最相容)
String addr = "sms://+" + phoneNumber.trim();
// 2. 開啟連線 (非阻塞,快速返回)
conn = (MessageConnection) Connector.open(addr);
// 3. 建立文本訊息
TextMessage msg = (TextMessage) conn.newMessage(
MessageConnection.TEXT_MESSAGE);
// 4. 設定內容 (自動處理 7-bit/8-bit/UCS-2 編碼)
msg.setPayloadText(content);
// 5. 發送 (非同步,立即返回)
conn.send(msg);
return true;
} catch (Exception e) {
// 常見異常: SecurityException(權限), IOException(無信號/格式錯誤),
// IllegalArgumentException(號碼格式)
return false;
} finally {
if (conn != null) try { conn.close(); } catch (IOException ignored) {}
}
}
}
權限與安全模型 (關鍵)
| 安全域 | 短信發送行為 |
| Untrusted (未簽名/自簽) | 每次發送彈出用戶確認對話框「允許發送短信嗎?」,用戶可拒絕。 |
| Trusted (CA 簽名) | 首次安裝提示授權,後續靜默發送 (視運營商策略)。 |
| Manufacturer/Operator | 完全靜默,無提示。 |
建議:遊戲社交功能預期用戶主動觸發 (按「邀請好友」),Untrusted 域下確認框可接受;自動發送 (如心跳) 需簽名。
進階:長短信與多段發送 (WMA 2.0)
// WMA 2.0 自動分段,開發者無感知
// 若需手動控制或讀取段數:
public static int getSegmentCount(String content) {
// 7-bit 編碼: 160 字/段, 中文 UCS-2: 70 字/段
// 簡易估算:
boolean hasChinese = content.matches(".*[\\u4e00-\\u9fa5].*");
int perSeg = hasChinese ? 70 : 160;
return (content.length() + perSeg - 1) / perSeg;
}
完整錯誤處理與重試模式
public class RobustSmsSender {
private static final int MAX_RETRY = 3;
private static final int RETRY_DELAY_MS = 2000;
public static boolean sendWithRetry(String phone, String text, SmsCallback cb) {
new Thread(() -> {
for (int attempt = 1; attempt <= MAX_RETRY; attempt++) {
try {
if (sendText(phone, text)) {
cb.onSuccess();
return;
}
} catch (Exception e) {
// 記錄日誌
}
if (attempt < MAX_RETRY) {
try { Thread.sleep(RETRY_DELAY_MS * attempt); }
catch (InterruptedException ignored) {}
}
}
cb.onFailed("超過重試次數");
}).start();
return true; // 非同步啟動
}
interface SmsCallback {
void onSuccess();
void onFailed(String reason);
}
}
接收短信 (雙向通訊)
// 需在 JAD 註冊 Push Registry
// MIDlet-Push-1: sms://:5000, com.mygame.SmsMidlet, *
public class SmsReceiver extends MIDlet implements MessageListener {
private MessageConnection conn;
protected void startApp() {
try {
conn = (MessageConnection) Connector.open("sms://:5000");
conn.setMessageListener(this);
} catch (IOException e) { /* 處理 */ }
}
public void notifyIncomingMessage(MessageConnection conn) {
// 在獨立執行緒回調,可直接處理
new Thread(() -> {
try {
Message msg = conn.receive();
if (msg instanceof TextMessage) {
String from = msg.getAddress(); // 發送者號碼
String text = ((TextMessage) msg).getPayloadText();
processIncomingSms(from, text);
}
} catch (IOException ignored) {}
}).start();
}
// ...
}
最佳實踐清單
- ✅ 號碼統一用國際格式
+86138xxxxxxxx (去除空格/連字符)。
- ✅ 發送前驗證號碼格式 (正則
^\\+?[1-9]\\d{6,14}$)。
- ✅ 內容長度預檢,超長提示用戶或自動截斷。
- ✅ 非同步發送 + 回調,不阻塞 UI 執行緒。
- ✅ 捕獲
SecurityException 友善提示「需授權發送短信」。
- ✅ 真機測試不同運營商/機型確認框行為差異。
- 上一篇文章: J2ME Canvas 字體進階:Font 三屬性組合、ChoiceGroup 多選風格與畫面清除最佳實踐
- 下一篇文章: J2ME 低層圖形系統深度解析:Display、Graphics、Canvas、Font、Image 五大核心類完整指南
Copyright ©
程式碼之家 All Rights Reserved