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

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();
        }

    }

}

问题原因分析

  1. SMS_SENT广播的局限性:android.provider.Telephony.SMS_SENT广播仅在当前应用自身发送短信时触发,系统短信应用或其他第三方应用发送的短信不会触发该广播。
  2. 代码逻辑错误:
    • 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 08:35:57