Android短信监控技能实现:合法合规的长途收罗方案 [复制链接]
发表于 2025-9-18 22:26:54 | 显示全部楼层 |阅读模式
一年经验的全栈步调员,目前头发健在,但不知道能撑多久。
  该项目已成功部署并稳固运行于企业生产环境,如需个性化定制方案,欢迎接洽作者举行深度互助。
  
   文章目次

  
  媒介
  一、页面筹划
  1.页面显示
   2.代码实现
   二、详细代码实现
  1.添加网络权限和短信权限
  2.实现短信监听(BroadcastReceiver)
  3.AndroidManifest.xml 中注册广播吸取器
  4. 封装网络哀求(HttpURLConnection) 
   三、MainActivity主步调编写
  1. 权限管理模块
  2. 短信吸取与处理处罚模块 
   3. 数据存储与展示模块
  4. 用户设置管理模块
  5. 定时整理模块(可选) 
   总结
  🙌 求点赞、收藏、关注! 
  
  媒介

  由于公司业务需求需要监控监控大批手机号的验证码以是有了这个项目,在 Android 应用开发中,短信监控监控通常用于合规场景,如企业装备管理、金融风控(验证码自动添补)或家长监护。差别于直接读取短信数据库(ContentProvider),利用 BroadcastReceiver 监听短信广播(android.provider.Telephony.SMS_RECEIVED)是一种更轻量、及时性更强的方案。
本文将介绍怎样通过 BroadcastReceiver 捕获短信,并利用 原生 HttpURLConnection(而非第三方库)将数据安全上传至服务器,涵盖以下关键点:

  • 短信监听机制:注册广播吸取器,过滤有用短信(如特定发送方或验证码)。
  • 网络哀求实现:手动封装 HttpURLConnection,支持 POST/GET 哀求。
  • 安全与合规性

    • 动态申请 RECEIVE_SMS 权限,确保用户知情同意。
    • 制止存储敏感信息,仅传输须要数据。

留意:未经用户授权的短信监控监控属于违法举动,本文仅限技能探究,请确保应用符合 Google Play 政策及《个人信息掩护法》。

一、页面筹划

由于没有做同一的日记管理但是也需要查察短信是否有监听到利用页面需要显示监听的手机号和内容。
1.页面显示


 2.代码实现

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     xmlns:app="http://schemas.android.com/apk/res-auto"
  4.     xmlns:tools="http://schemas.android.com/tools"
  5.     android:layout_width="match_parent"
  6.     android:layout_height="match_parent"
  7.     tools:context=".MainActivity">
  8.     <!-- 应用名称标题 -->
  9.     <TextView
  10.         android:id="@+id/appNameTextView"
  11.         android:layout_width="wrap_content"
  12.         android:layout_height="wrap_content"
  13.         android:padding="16dp"
  14.         android:text="短信接收器"
  15.         android:textSize="20sp"
  16.         android:textStyle="bold"
  17.         app:layout_constraintLeft_toLeftOf="parent"
  18.         app:layout_constraintRight_toRightOf="parent"
  19.         app:layout_constraintTop_toTopOf="parent" />
  20.     <!-- 卡1标题 -->
  21.     <Button
  22.         android:id="@+id/baoc"
  23.         android:layout_width="wrap_content"
  24.         android:layout_height="wrap_content"
  25.         android:layout_marginEnd="16dp"
  26.         android:onClick="save"
  27.         android:text="保存"
  28.         app:layout_constraintBottom_toBottomOf="@+id/appNameTextView"
  29.         app:layout_constraintRight_toRightOf="parent"
  30.         app:layout_constraintTop_toTopOf="@+id/appNameTextView" />
  31.     <TextView
  32.         android:id="@+id/card1TitleTextView"
  33.         android:layout_width="wrap_content"
  34.         android:layout_height="wrap_content"
  35.         android:layout_marginStart="16dp"
  36.         android:layout_marginTop="16dp"
  37.         android:text="卡1:"
  38.         android:textSize="16sp"
  39.         android:textStyle="bold"
  40.         app:layout_constraintBottom_toBottomOf="@+id/editTextPhone1"
  41.         app:layout_constraintLeft_toLeftOf="parent"
  42.         app:layout_constraintTop_toBottomOf="@+id/appNameTextView" />
  43.     <!-- 卡1输入框 -->
  44.     <EditText
  45.         android:id="@+id/editTextPhone1"
  46.         android:layout_width="0dp"
  47.         android:layout_height="wrap_content"
  48.         android:layout_marginStart="8dp"
  49.         android:layout_marginEnd="8dp"
  50.         android:hint="输入卡1号码"
  51.         android:textSize="16sp"
  52.         app:layout_constraintLeft_toRightOf="@+id/card1TitleTextView"
  53.         app:layout_constraintRight_toLeftOf="@+id/switch1"
  54.         app:layout_constraintTop_toTopOf="@+id/card1TitleTextView" />
  55.     <!-- 卡1开关 -->
  56.     <Switch
  57.         android:id="@+id/switch1"
  58.         android:layout_width="wrap_content"
  59.         android:layout_height="wrap_content"
  60.         android:layout_marginEnd="16dp"
  61.         app:layout_constraintBottom_toBottomOf="@+id/editTextPhone1"
  62.         app:layout_constraintRight_toRightOf="parent"
  63.         app:layout_constraintTop_toTopOf="@+id/editTextPhone1" />
  64.     <!-- 卡2标题 -->
  65.     <TextView
  66.         android:id="@+id/card2TitleTextView"
  67.         android:layout_width="wrap_content"
  68.         android:layout_height="wrap_content"
  69.         android:layout_marginStart="16dp"
  70.         android:layout_marginTop="16dp"
  71.         android:text="卡2:"
  72.         android:textSize="16sp"
  73.         android:textStyle="bold"
  74.         app:layout_constraintBottom_toBottomOf="@+id/editTextPhone2"
  75.         app:layout_constraintLeft_toLeftOf="parent"
  76.         app:layout_constraintTop_toBottomOf="@+id/editTextPhone1" />
  77.     <!-- 卡2输入框 -->
  78.     <EditText
  79.         android:id="@+id/editTextPhone2"
  80.         android:layout_width="0dp"
  81.         android:layout_height="wrap_content"
  82.         android:layout_marginStart="8dp"
  83.         android:layout_marginEnd="8dp"
  84.         android:hint="输入卡2号码"
  85.         android:textSize="16sp"
  86.         app:layout_constraintLeft_toRightOf="@+id/card2TitleTextView"
  87.         app:layout_constraintRight_toLeftOf="@+id/switch2"
  88.         app:layout_constraintTop_toTopOf="@+id/card2TitleTextView" />
  89.     <!-- 卡2开关 -->
  90.     <Switch
  91.         android:id="@+id/switch2"
  92.         android:layout_width="wrap_content"
  93.         android:layout_height="wrap_content"
  94.         android:layout_marginEnd="16dp"
  95.         app:layout_constraintBottom_toBottomOf="@+id/editTextPhone2"
  96.         app:layout_constraintRight_toRightOf="parent"
  97.         app:layout_constraintTop_toTopOf="@+id/editTextPhone2" />
  98.     <!-- 短信内容显示框 -->
  99.     <TextView
  100.         android:id="@+id/smsTextView"
  101.         android:layout_width="0dp"
  102.         android:layout_height="300dp"
  103.         android:layout_marginTop="8dp"
  104.         android:background="#000000"
  105.         android:padding="8dp"
  106.         android:text="监控短信中"
  107.         android:textColor="#FFFFFF"
  108.         android:scrollbars="vertical"
  109.         app:layout_constraintLeft_toLeftOf="parent"
  110.         app:layout_constraintRight_toRightOf="parent"
  111.         app:layout_constraintTop_toBottomOf="@+id/editTextPhone2" />
  112. </androidx.constraintlayout.widget.ConstraintLayout>
复制代码
为什么这里需要设置卡号显示起除是由于安卓开发体系的匹配度问题有些手机体系是不能自动辨认手机号的以是显示这个卡号是看看有没有自动辨认,如果没有需要手动输入而且生存,以为背面是根据详细卡槽id辨认是哪个手机号吸取到的验证码。

 二、详细代码实现

1.添加网络权限和短信权限

在AndroidManifest.xml
  1. <uses-permission android:name="android.permission.INTERNET" />
  2. <uses-permission android:name="android.permission.RECEIVE_SMS" />
复制代码

2.实现短信监听(BroadcastReceiver)

新建一个SMSReceiver.class服务监听短信
  1. public class SMSReceiver extends BroadcastReceiver {
  2.     @Override
  3.     public void onReceive(Context context, Intent intent) {
  4.         // 1. 从广播意图中提取短信数据
  5.         Bundle bundle = intent.getExtras();
  6.         if (bundle != null) {
  7.             // 2. 获取短信PDU数组和SIM卡订阅ID
  8.             Object[] pdus = (Object[]) bundle.get("pdus");
  9.             int subscriptionId = bundle.getInt("subscription", -1);
  10.             
  11.             if (pdus != null) {
  12.                 for (Object pdu : pdus) {
  13.                     // 3. 解析短信内容(发送方、正文)
  14.                     SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdu);
  15.                     String messageBody = smsMessage.getMessageBody();
  16.                     String sender = smsMessage.getDisplayOriginatingAddress();
  17.                     
  18.                     // 4. 获取SIM卡槽位信息(双卡场景)
  19.                     String slotInfo = getSlotInfo(context, subscriptionId);
  20.                     
  21.                     // 5. 发送自定义广播传递短信数据
  22.                     Intent smsIntent = new Intent("smsReceived");
  23.                     smsIntent.putExtra("message", messageBody);
  24.                     smsIntent.putExtra("sender", sender);
  25.                     smsIntent.putExtra("slotInfo", slotInfo);
  26.                     context.sendBroadcast(smsIntent);
  27.                 }
  28.             }
  29.         }
  30.     }
  31.     /**
  32.      * 获取SIM卡槽位信息(兼容双卡)
  33.      * @param subscriptionId SIM卡订阅ID
  34.      * @return 如 "Slot 1" 或 "Unknown Slot"
  35.      */
  36.     private String getSlotInfo(Context context, int subscriptionId) {
  37.         // 实现逻辑:通过SubscriptionManager查询对应SIM卡槽
  38.         // ...
  39.     }
  40.     /**
  41.      * 获取接收方手机号(需权限)
  42.      * @note 因权限问题已注释,实际使用需动态申请READ_PHONE_STATE权限
  43.      */
  44.     @RequiresApi(api = Build.VERSION_CODES.N)
  45.     private String getReceiverPhoneNumber(Context context, int subscriptionId) {
  46.         // 实现逻辑:通过TelephonyManager获取本机号码
  47.         // ...
  48.     }
  49. }
复制代码
3.AndroidManifest.xml 中注册广播吸取器

  1. <receiver android:name=".SMSReceiver"
  2.             android:exported="true"
  3.             android:enabled="true">
  4.             <intent-filter>
  5.                 <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
  6.             </intent-filter>
  7.         </receiver>
复制代码
4. 封装网络哀求(HttpURLConnection) 

  1. public class MyRequest {
  2.     public String post(String url1, String data) {
  3.         try {
  4.             URL url = new URL(url1);
  5.             HttpURLConnection Connection = (HttpURLConnection) url.openConnection();//创建连接
  6.             Connection.setRequestMethod("POST");
  7.             Connection.setConnectTimeout(3000);
  8.             Connection.setReadTimeout(3000);
  9.             Connection.setDoInput(true);
  10.             Connection.setDoOutput(true);
  11.             Connection.setUseCaches(false);
  12.             Connection.connect();
  13.             DataOutputStream dos = new DataOutputStream(Connection.getOutputStream());
  14.             String title = data;//这里是POST请求需要的参数字符串类型,例如"id=1&data=2"
  15.             dos.write(title.getBytes());
  16.             dos.flush();
  17.             dos.close();//写完记得关闭
  18.             int responseCode = Connection.getResponseCode();
  19.             if (responseCode == Connection.HTTP_OK) {//判断请求是否成功
  20.                 InputStream inputStream = Connection.getInputStream();
  21.                 ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
  22.                 byte[] bytes = new byte[1024];
  23.                 int length = 0;
  24.                 while ((length = inputStream.read(bytes)) != -1) {
  25.                     arrayOutputStream.write(bytes, 0, length);
  26.                     arrayOutputStream.flush();
  27.                 }//读取响应体的内容
  28.                 String s = arrayOutputStream.toString();
  29.                 return s;//返回请求到的内容,字符串形式
  30.             } else {
  31.                 return "-1";//如果请求失败返回-1
  32.             }
  33.         } catch (Exception e) {
  34.             return "-1";//出现异常也返回-1
  35.         }
  36.     }
  37.     public String get(String url1) {
  38.         try {
  39.             URL url = new URL(url1);
  40.             HttpURLConnection Connection = (HttpURLConnection) url.openConnection();
  41.             Connection.setRequestMethod("GET");
  42.             Connection.setConnectTimeout(3000);
  43.             Connection.setReadTimeout(3000);
  44.             int responseCode = Connection.getResponseCode();
  45.             if (responseCode == Connection.HTTP_OK) {
  46.                 InputStream inputStream = Connection.getInputStream();
  47.                 ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
  48.                 byte[] bytes = new byte[1024];
  49.                 int length = 0;
  50.                 while ((length = inputStream.read(bytes)) != -1) {
  51.                     arrayOutputStream.write(bytes, 0, length);
  52.                     arrayOutputStream.flush();//强制释放缓冲区
  53.                 }
  54.                 String s = arrayOutputStream.toString();
  55.                 return s;
  56.             } else {
  57.                 return "-1";
  58.             }
  59.         } catch (Exception e) {
  60.             return "-1"+e;
  61.         }
  62.     }
  63. }
复制代码



 三、MainActivity主步调编写

1. 权限管理模块

  1. // 定义所需权限
  2. private static final int PERMISSION_REQUEST_CODE = 1;
  3. String[] permissions = {
  4.     Manifest.permission.READ_SMS,
  5.     Manifest.permission.RECEIVE_SMS,
  6.     Manifest.permission.READ_PHONE_STATE,
  7.     Manifest.permission.READ_PHONE_NUMBERS
  8. };
  9. // 检查并请求权限
  10. if (未全部授权) {
  11.     ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
  12. } else {
  13.     registerSmsReceiver(); // 注册广播接收器
  14. }
  15. // 权限请求结果回调
  16. @Override
  17. public void onRequestPermissionsResult(...) {
  18.     if (权限通过) {
  19.         registerSmsReceiver();
  20.     } else {
  21.         showToast("短信监控功能不可用");
  22.     }
  23. }
复制代码
2. 短信吸取与处理处罚模块 

  1. // 自定义广播接收器
  2. private BroadcastReceiver smsReceiver = new BroadcastReceiver() {
  3.     @Override
  4.     public void onReceive(Context context, Intent intent) {
  5.         // 解析短信数据
  6.         String message = intent.getStringExtra("message");
  7.         String sender = intent.getStringExtra("sender");
  8.         String slotInfo = intent.getStringExtra("slotInfo");
  9.         
  10.         // 根据SIM卡槽匹配接收号码
  11.         String receiverNumber = slotInfo.contains("1") ?
  12.             editTextPhone1.getText().toString() :
  13.             editTextPhone2.getText().toString();
  14.         
  15.         // 过滤重复消息
  16.         if (pdMessages(message)) {
  17.             // 启动网络请求线程
  18.             new Thread(() -> {
  19.                 String url = "服务器接口;
  20.                 String response = new MyRequest().get(url);
  21.                 handleResponse(response, receiverNumber, sender, message);
  22.             }).start();
  23.         }
  24.     }
  25. };
  26. // 消息去重检查
  27. private boolean pdMessages(String mess) {
  28.     Set<String> savedMessages = sp.getStringSet("messages", new HashSet<>());
  29.     if (savedMessages.contains(mess)) {
  30.         return false; // 重复消息
  31.     }
  32.     savedMessages.add(mess);
  33.     sp.edit().putStringSet("messages", savedMessages).apply();
  34.     return true;
  35. }
复制代码
 3. 数据存储与展示模块

  1. // 存储消息记录(包含状态和时间戳)
  2. private void storeMessageStatus(String receiver, String sender, String message, String status) {
  3.     String key = "message_" + System.currentTimeMillis();
  4.     String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
  5.    
  6.     String record = String.format(
  7.         "接收号: %s\n发送方: %s\n时间: %s\n内容: %s\n状态: %s\n",
  8.         receiver, sender, time, message, status
  9.     );
  10.    
  11.     sp.edit()
  12.         .putString(key + "_data", record)
  13.         .putLong(key + "_time", System.currentTimeMillis())
  14.         .apply();
  15. }
  16. // 显示历史消息
  17. private void displayStoredMessages() {
  18.     StringBuilder sb = new StringBuilder();
  19.     for (Map.Entry<String, ?> entry : sp.getAll().entrySet()) {
  20.         if (entry.getKey().endsWith("_data")) {
  21.             sb.append(entry.getValue()).append("\n");
  22.         }
  23.     }
  24.     smsTextView.setText(sb.length() > 0 ? sb.toString() : "无记录");
  25. }
复制代码
4. 用户设置管理模块

  1. // 保存用户设置的手机号
  2. public void save(View view) {
  3.     sp.edit()
  4.         .putString("editTextPhone1", editTextPhone1.getText().toString())
  5.         .putString("editTextPhone2", editTextPhone2.getText().toString())
  6.         .apply();
  7.     showToast("保存成功");
  8. }
  9. // 初始化时加载配置
  10. private void loadConfig() {
  11.     editTextPhone1.setText(sp.getString("editTextPhone1", ""));
  12.     editTextPhone2.setText(sp.getString("editTextPhone2", ""));
  13. }
复制代码
5. 定时整理模块(可选) 

  1. // 设置每天中午12点清理旧数据
  2. private void setDailyCleanupAlarm() {
  3.     AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
  4.     Intent intent = new Intent(this, CleanupReceiver.class);
  5.     PendingIntent pendingIntent = PendingIntent.getBroadcast(...);
  6.    
  7.     Calendar calendar = Calendar.getInstance();
  8.     calendar.set(Calendar.HOUR_OF_DAY, 12); // 12:00 PM
  9.     if (已过当前时间) calendar.add(Calendar.DAY_OF_YEAR, 1);
  10.    
  11.     alarmManager.setExactAndAllowWhileIdle(
  12.         AlarmManager.RTC_WAKEUP,
  13.         calendar.getTimeInMillis(),
  14.         pendingIntent
  15.     );
  16. }
复制代码

 总结


  • 实现了双卡短信监控:通过BroadcastReceiver捕获短信,并根据SIM卡槽自动匹配预设的手机号,支持双卡场景下的短信分类处理处罚。
  • 数据安全与合规性:动态申请权限确保用户知情权,利用SharedPreferences存储消息记载,制止敏感信息走漏,符合隐私掩护要求。
  • 网络上传与状态反馈:通过HttpURLConnection将短信内容上传至服务器,并及时显示发送状态(成功/失败),数据长期化便于追溯。
  • 可扩展性强:模块化筹划(权限管理、消息处理处罚、数据存储)便于后续扩展,如增长加密传输或对接其他API
🙌 求点赞、收藏、关注! 

如果这篇文章对你有资助,不妨:
👍 点个赞 → 让更多人看到这篇干货!
⭐ 收藏一下 → 方便以后随时查阅!
🔔 加关注 → 获取更多 前端/后端/全栈技能深度剖析
你的支持,是我连续创作的最大动力! 🚀

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表