Android应用仅捕获接收短信,无法捕获发送短信求助
问题:Android应用无法监听发送的短信
我正在开发一款Android应用,需求是监听设备的接收和发送短信,将短信内容格式化为JSON后发送至本地Flask服务器。已完成以下配置:
- 在
AndroidManifest.xml中声明RECEIVE_SMS、READ_SMS、SEND_SMS、INTERNET、FOREGROUND_SERVICE权限 - 将
SmsListenerService设为带持久通知的前台服务 - 在服务内配置
BroadcastReceiver,监听Telephony.Sms.Intents.SMS_RECEIVED_ACTION和android.provider.Telephony.SMS_SENT广播
目前接收短信的JSON生成及服务器发送功能正常,但发送短信时无触发,仅能捕获接收短信,实体设备测试多次未解决。以下是SmsListenerService代码片段:
package de.dlyt.yanndroid.notifer.service; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Build; import android.os.IBinder; import android.provider.Telephony; import android.telephony.SmsMessage; import android.util.Log; import androidx.core.app.NotificationCompat; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import de.dlyt.yanndroid.notifer.MainActivity; import de.dlyt.yanndroid.notifer.R; public class SmsListenerService extends Service { private static final String TAG = "SmsListenerService"; private static final String CHANNEL_ID = "sms_listener_channel"; private static final int NOTIFICATION_ID = 1; private BroadcastReceiver smsReceiver = new SMSReceiver(); @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { createNotificationChannel(); Intent notificationIntent = new Intent(this, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE); Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("SMS Listener") .setContentText("Listening for SMS...") // .setSmallIcon(R.drawable.notification_icon) .setContentIntent(pendingIntent) .build(); startForeground(NOTIFICATION_ID, notification); return START_STICKY; } private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { CharSequence name = "SMS Listener Channel"; String description = "Channel for SMS Listener notifications"; int importance = NotificationManager.IMPORTANCE_DEFAULT; NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance); channel.setDescription(description); NotificationManager notificationManager = getSystemService(NotificationManager.class); notificationManager.createNotificationChannel(channel); } } @Override public void onCreate() { super.onCreate(); Log.d(TAG, "SMS Service created"); IntentFilter filter = new IntentFilter(); filter.addAction(Telephony.Sms.Intents.SMS_RECEIVED_ACTION); filter.addAction("android.provider.Telephony.SMS_SENT"); registerReceiver(smsReceiver, filter, Context.RECEIVER_EXPORTED); } @Override public void onDestroy() { super.onDestroy(); unregisterReceiver(smsReceiver); } public static class SMSReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); SmsMessage[] smsMessages = Telephony.Sms.Intents.getMessagesFromIntent(intent); String messageBody = smsMessages[0].getMessageBody(); Log.d("Andrew","Hello"); Log.d("Andrew",messageBody); // if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(action) || // "android.provider.Telephony.SMS_SENT".equals(action)) { if (true){ for (SmsMessage smsMessage : Telephony.Sms.Intents.getMessagesFromIntent(intent)) { String address = smsMessage.getOriginatingAddress(); String body = smsMessage.getMessageBody(); long timestamp = smsMessage.getTimestampMillis(); JSONObject jsonSms = new JSONObject(); String deviceName = Build.MODEL; try { jsonSms.put("deviceName", deviceName != null ? deviceName : "Unknown Device"); jsonSms.put("appName", Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(action) ? "incoming" : "outgoing"); jsonSms.put("packageName", Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(action) ? "incoming" : "outgoing"); jsonSms.put("id",0); jsonSms.put("time", timestamp); jsonSms.put("isOngoing",false); jsonSms.put("Notification Extra","" jsonSms.put("title", address); jsonSms.put("text", body); // Added "body" as the key jsonSms.put("subText", ""); jsonSms.put("titleBig", ""); jsonSms.put("bigText", ""); jsonSms.put("progressIndeterminate", 0); jsonSms.put("progressMax",0); jsonSms.put("progress",0); jsonSms.put("interruptionFilter",0); // Send the JSON object to your server sendToServer(jsonSms.toString(), context); } catch (JSONException e) { Log.e(TAG, "Error creating JSON object", e); } } } } private static void sendToServer(String jsonData, Context context) { String serverUrl = "http://**.***.**.***:8000"; // Your server URL new Thread(() -> { HttpURLConnection conn = null; try { URL url = new URL(serverUrl); conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/json"); conn.setDoOutput(true); try(OutputStream os = conn.getOutputStream()) { byte[] input = jsonData.getBytes(StandardCharsets.UTF_8); os.write(input, 0, input.length); } int responseCode = conn.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { Log.d(TAG, "SMS data sent successfully."); BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8")); StringBuilder response = new StringBuilder(); String responseLine = null; while ((responseLine = br.readLine()) != null) { response.append(responseLine.trim()); } Log.d(TAG, "Server response: " + response.toString()); } else { Log.e(TAG, "Error sending SMS data. Response Code: " + responseCode); } } catch (IOException e) { Log.e(TAG, "IOException while sending SMS data:", e); } finally { if (conn != null) { conn.disconnect(); } } }).start(); } } }
问题原因分析
SMS_SENT广播的局限性:android.provider.Telephony.SMS_SENT广播仅在当前应用自身发送短信时触发,系统短信应用或其他第三方应用发送的短信不会触发该广播。- 代码逻辑错误:
SMS_SENT广播的Intent中不携带完整短信消息对象,调用Telephony.Sms.Intents.getMessagesFromIntent(intent)会导致无效数据或空指针异常。- 代码存在语法错误:
jsonSms.put("Notification Extra",""缺少闭合括号与引号,会直接抛出JSON构建异常,中断后续逻辑。
解决方案
方案1:监听短信数据库变化(推荐,支持所有应用发送的短信)
所有发送的短信都会写入系统短信数据库,通过ContentObserver监听content://sms/sent URI的变化,即可捕获所有已发送短信:
代码修改示例
在SmsListenerService的onCreate方法中添加ContentObserver注册:
import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Handler; import android.os.Looper; @Override public void onCreate() { super.onCreate(); Log.d(TAG, "SMS Service created"); // 注册接收短信的广播 IntentFilter filter = new IntentFilter(); filter.addAction(Telephony.Sms.Intents.SMS_RECEIVED_ACTION); registerReceiver(smsReceiver, filter, Context.RECEIVER_EXPORTED); // 注册已发送短信的ContentObserver getContentResolver().registerContentObserver( Uri.parse("content://sms/sent"), true, new SentSmsObserver(new Handler(Looper.getMainLooper()), this) ); } // 自定义ContentObserver监听已发送短信 private static class SentSmsObserver extends ContentObserver { private Context context; public SentSmsObserver(Handler handler, Context context) { super(handler); this.context = context; } @Override public void onChange(boolean selfChange) { super.onChange(selfChange); // 查询最新的已发送短信 queryLatestSentSms(context); } private void queryLatestSentSms(Context context) { String[] projection = { Telephony.Sms._ID, Telephony.Sms.ADDRESS, Telephony.Sms.BODY, Telephony.Sms.DATE }; // 按时间倒序,只取最新一条 String sortOrder = Telephony.Sms.DATE + " DESC LIMIT 1"; try (Cursor cursor = context.getContentResolver().query( Telephony.Sms.SENT_URI, projection, null, null, sortOrder)) { if (cursor != null && cursor.moveToFirst()) { String address = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.ADDRESS)); String body = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.BODY)); long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(Telephony.Sms.DATE)); // 构建JSON并发送到服务器 JSONObject jsonSms = new JSONObject(); String deviceName = Build.MODEL; try { jsonSms.put("deviceName", deviceName != null ? deviceName : "Unknown Device"); jsonSms.put("appName", "outgoing"); jsonSms.put("packageName", "outgoing"); jsonSms.put("id",0); jsonSms.put("time", timestamp); jsonSms.put("isOngoing",false); jsonSms.put("Notification Extra",""); jsonSms.put("title", address); jsonSms.put("text", body); jsonSms.put("subText", ""); jsonSms.put("titleBig", ""); jsonSms.put("bigText", ""); jsonSms.put("progressIndeterminate", 0); jsonSms.put("progressMax",0); jsonSms.put("progress",0); jsonSms.put("interruptionFilter",0); SMSReceiver.sendToServer(jsonSms.toString(), context); } catch (JSONException e) { Log.e(TAG, "Error creating JSON object", e); } } } catch (SecurityException e) { Log.e(TAG, "Permission denied to read SMS", e); } } }
方案2:修正SMS_SENT广播逻辑(仅适用于自身应用发送的短信)
如果只需要监听当前应用发送的短信,需在发送短信时指定PendingIntent,并修正广播接收逻辑:
// 发送短信时的代码示例 String phoneNumber = "1234567890"; String message = "Test message"; PendingIntent sentPI = PendingIntent.getBroadcast(context, 0, new Intent("android.provider.Telephony.SMS_SENT"), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); SmsManager smsManager = SmsManager.getDefault(); smsManager.sendTextMessage(phoneNumber, null, message, sentPI, null);
同时修改SMSReceiver的onReceive方法:
@Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(action)) { // 处理接收短信逻辑(保留原代码) SmsMessage[] smsMessages = Telephony.Sms.Intents.getMessagesFromIntent(intent); for (SmsMessage smsMessage : smsMessages) { // 原接收短信处理逻辑 } } else if ("android.provider.Telephony.SMS_SENT".equals(action)) { // 处理发送短信结果 switch (getResultCode()) { case Activity.RESULT_OK: // 发送成功,这里需要自己保存发送的短信内容并构建JSON break; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: case SmsManager.RESULT_ERROR_NO_SERVICE: Log.e(TAG, "SMS send failed"); break; } } }
基础错误修复
修正原代码中的JSON语法错误:
// 原错误代码 // jsonSms.put("Notification Extra","" // 修改为 jsonSms.put("Notification Extra","");
注意事项
- Android 10及以上版本,读取短信需要动态申请
READ_SMS权限,需引导用户授权。 ContentObserver可能重复触发,建议通过短信ID去重,避免重复发送数据。- 测试时需确保应用已授予所有必要权限。
内容的提问来源于stack exchange,提问作者Andrew Moreno
相关产品推荐
相关产品推荐

