Android Studio接收SMS时无限循环及无法添加DBHandler至onReceive的问题
Android SMS自动回复项目问题解决方案
问题1:接收SMS时陷入无限循环
原因
你的SmsListener会监听所有收到的短信,包括程序自身发送的回复短信。每次收到短信触发回复逻辑,回复的短信又会被自身的广播接收器捕获,进而再次触发回复,形成无限循环。
解决方案
在回复短信中添加专属标记,收到短信时先检查是否包含该标记,若包含则直接跳过处理,避免触发循环。同时,将回复逻辑移到后台服务中,避免在广播接收器中直接执行耗时操作。
问题2:无法在onReceive中使用DBHandler
原因
BroadcastReceiver的onReceive方法生命周期极短,且运行在主线程,直接在这里执行数据库查询、循环计算等操作,不仅容易引发ANR(应用无响应),还会因为生命周期限制导致DB操作不稳定。原代码中把DB操作放在MainActivity的Eye()方法里,通过启动Activity来处理的方式既不合理,又加重了循环问题。
解决方案
使用IntentService处理后台任务:广播接收器只负责接收短信并传递数据,实际的DB计算、短信回复逻辑全部交给IntentService完成,这样既符合Android组件生命周期规范,也能避免主线程阻塞。
修正后代码示例
1. 修正SmsListener.java(广播接收器)
public class SmsListener extends BroadcastReceiver { // 自动回复标记,用于过滤自身发送的短信 private static final String AUTO_REPLY_TAG = "[系统自动回复]"; @Override public void onReceive(Context context, Intent intent) { if (!"android.provider.Telephony.SMS_RECEIVED".equals(intent.getAction())) { return; } Bundle bundle = intent.getExtras(); if (bundle == null) return; try { Object[] pdus = (Object[]) bundle.get("pdus"); String format = bundle.getString("format"); SmsMessage[] msgs = new SmsMessage[pdus.length]; for (int i = 0; i < msgs.length; i++) { msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i], format); String msgFrom = msgs[i].getOriginatingAddress(); String msgBody = msgs[i].getMessageBody(); // 过滤自身发送的自动回复短信,终止循环 if (msgBody.contains(AUTO_REPLY_TAG)) { continue; } // 识别预约请求,启动后台服务处理 if (msgBody.contains("Appt")) { Intent serviceIntent = new Intent(context, SmsProcessingService.class); serviceIntent.putExtra("sender", msgFrom); serviceIntent.putExtra("content", msgBody); ContextCompat.startForegroundService(context, serviceIntent); } } } catch (Exception e) { Log.d("SmsListener", "异常捕获:" + e.getMessage()); } } }
2. 新增SmsProcessingService.java(后台处理服务)
public class SmsProcessingService extends IntentService { private static final String AUTO_REPLY_TAG = "[系统自动回复]"; public SmsProcessingService() { super("SmsProcessingService"); } @Override protected void onHandleIntent(Intent intent) { if (intent == null) return; String sender = intent.getStringExtra("sender"); String content = intent.getStringExtra("content"); // 初始化DBHandler,执行日期计算 DBHandler dbHandler = new DBHandler(this); Calendar c1 = Calendar.getInstance(); c1.add(Calendar.DAY_OF_MONTH, 1); SimpleDateFormat sdf = new SimpleDateFormat("MMM_dd_yyyy", Locale.getDefault()); String dt = sdf.format(c1.getTime()); int maxCount = 2; // 计算可用预约日期 while (dbHandler.Count_Inv(sdf.format(c1.getTime()) + "_Eye", "Eye", dt, "Eye") > maxCount) { c1.add(Calendar.DAY_OF_MONTH, 1); dt = sdf.format(c1.getTime()); } // 计算预约时段 Long count = dbHandler.Count_Eye(sdf.format(c1.getTime()) + "_Eye", "Eye", dt); int slotIndex = count.intValue(); double slot = 0; switch (slotIndex) { case 0: slot = 9.00; break; case 1: slot = 9.15; break; case 2: slot = 9.30; break; case 3: slot = 9.45; break; case 4: slot = 10.00; break; case 5: slot = 10.15; break; } // 添加预约记录到数据库 dbHandler.addNewApptEye(sender, sdf.format(c1.getTime()) + "_Eye", dt, sdf.format(c1.getTime()), count); // 发送带标记的自动回复短信 String replyMsg = AUTO_REPLY_TAG + "您的眼科预约时间:" + dt + " " + slot + "点"; SmsManager smsManager = SmsManager.getDefault(); smsManager.sendTextMessage(sender, null, replyMsg, null, null); // 可选:启动MainActivity展示结果 Intent activityIntent = new Intent(this, MainActivity.class); activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(activityIntent); } }
3. 修正MainActivity.java(移除自动执行逻辑)
public class MainActivity extends AppCompatActivity { EditText mobileno; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 移除自动执行的Eye(),改为手动触发(比如按钮点击) } // 保留手动预约逻辑,供界面触发使用 public void manualBook(View view) { DBHandler dbHandler = new DBHandler(this); Calendar c1 = Calendar.getInstance(); c1.add(Calendar.DAY_OF_MONTH, 1); SimpleDateFormat sdf = new SimpleDateFormat("MMM_dd_yyyy", Locale.getDefault()); String dt = sdf.format(c1.getTime()); int maxCount = 2; try { mobileno = findViewById(R.id.editText1); maxCount = Integer.parseInt(mobileno.getText().toString()); } catch (Exception e) { e.printStackTrace(); } while (dbHandler.Count_Inv(sdf.format(c1.getTime()) + "_Eye", "Eye", dt, "Eye") > maxCount) { c1.add(Calendar.DAY_OF_MONTH, 1); dt = sdf.format(c1.getTime()); } Long count = dbHandler.Count_Eye(sdf.format(c1.getTime()) + "_Eye", "Eye", dt); int slotIndex = count.intValue(); double slot = 0; switch (slotIndex) { case 0: slot = 9.00; break; case 1: slot = 9.15; break; case 2: slot = 9.30; break; case 3: slot = 9.45; break; case 4: slot = 10.00; break; case 5: slot = 10.15; break; } String phone = mobileno.getText().toString(); dbHandler.addNewApptEye(phone, sdf.format(c1.getTime()) + "_Eye", dt, sdf.format(c1.getTime()), count); Toast.makeText(this, "预约成功:" + dt + " " + slot + "点", Toast.LENGTH_SHORT).show(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } }
额外注意事项
- 权限配置:在
AndroidManifest.xml中添加必要权限并注册组件:
<uses-permission android:name="android.permission.RECEIVE_SMS"/> <uses-permission android:name="android.permission.SEND_SMS"/> <receiver android:name=".SmsListener"> <intent-filter android:priority="1000"> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> </intent-filter> </receiver> <service android:name=".SmsProcessingService"/>
- 动态权限申请:Android 6.0及以上版本,需在代码中动态申请
RECEIVE_SMS和SEND_SMS权限。 - 内存泄漏避免:移除原代码中使用静态变量传递短信内容的方式,改用Intent传递数据。
内容的提问来源于stack exchange,提问作者Hashim
相关产品推荐
相关产品推荐

