Android开发:每隔N秒检测网络连接及短信触发发邮件的实现方法
Hey,刚好这两个需求我之前在项目里都实现过,给你整理了适配Android新版本的具体方案,都是经过实际测试可行的:
一、每隔N秒检测网络连接状态
这里推荐两种方案,根据你的使用场景选择:
方案1:使用WorkManager(优先推荐,适配Android 8+后台限制)
现在Android后台管控越来越严,Handler在后台很容易被系统杀死,WorkManager是Google官方推荐的后台任务调度工具,能保证任务在合适的时机执行。
步骤:
- 先添加WorkManager依赖(在
build.gradle(app)中):
dependencies { implementation "androidx.work:work-runtime:2.8.1" }
- 实现自定义Worker类,封装网络检测逻辑:
public class NetworkCheckWorker extends Worker { private static final String TAG = "NetworkCheckWorker"; public NetworkCheckWorker(@NonNull Context context, @NonNull WorkerParameters params) { super(context, params); } @NonNull @Override public Result doWork() { // 检测当前网络状态 boolean isConnected = checkNetworkStatus(getApplicationContext()); Log.d(TAG, "当前网络状态:" + (isConnected ? "已连接" : "未连接")); // 这里可以根据状态做你需要的操作,比如发送通知、同步数据等 // 如果需要持续循环检测,调度下一次任务(WorkManager的PeriodicWorkRequest最小间隔是15分钟,短间隔用这个方式) long interval = getInputData().getLong("CHECK_INTERVAL", 10000); // 默认10秒 OneTimeWorkRequest nextCheck = new OneTimeWorkRequest.Builder(NetworkCheckWorker.class) .setInitialDelay(interval, TimeUnit.MILLISECONDS) .setInputData(new Data.Builder() .putLong("CHECK_INTERVAL", interval) .build()) .build(); WorkManager.getInstance(getApplicationContext()).enqueue(nextCheck); return Result.success(); } // 工具方法:检测网络连接 private boolean checkNetworkStatus(Context context) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); if (cm == null) return false; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Network network = cm.getActiveNetwork(); NetworkCapabilities capabilities = cm.getNetworkCapabilities(network); return capabilities != null && (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)); } else { NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); return activeNetwork != null && activeNetwork.isConnectedOrConnecting(); } } }
- 在Activity或Application中启动检测任务:
// 设置检测间隔为10秒(可自行修改) long checkInterval = 10000; OneTimeWorkRequest initialCheck = new OneTimeWorkRequest.Builder(NetworkCheckWorker.class) .setInputData(new Data.Builder() .putLong("CHECK_INTERVAL", checkInterval) .build()) .build(); WorkManager.getInstance(this).enqueue(initialCheck);
注意点:
- 需要在
AndroidManifest.xml中添加网络权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- 如果检测间隔大于等于15分钟,直接用
PeriodicWorkRequest更简洁,不用手动调度下一次任务。
方案2:Handler+Runnable(适合前台持续运行场景)
如果你的App一直在前台运行,用Handler更轻量,不需要依赖额外库:
private Handler mNetworkHandler = new Handler(Looper.getMainLooper()); private Runnable mCheckRunnable; private final long CHECK_INTERVAL = 10000; // 10秒 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mCheckRunnable = new Runnable() { @Override public void run() { boolean isConnected = checkNetworkStatus(MainActivity.this); Log.d("NetworkCheck", "当前网络:" + (isConnected ? "正常" : "断开")); // 循环执行 mNetworkHandler.postDelayed(this, CHECK_INTERVAL); } }; // 启动检测 mNetworkHandler.post(mCheckRunnable); } // 页面销毁时一定要停止,避免内存泄漏 @Override protected void onDestroy() { super.onDestroy(); mNetworkHandler.removeCallbacks(mCheckRunnable); } // 同上面的checkNetworkStatus方法 private boolean checkNetworkStatus(Context context) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); if (cm == null) return false; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Network network = cm.getActiveNetwork(); NetworkCapabilities capabilities = cm.getNetworkCapabilities(network); return capabilities != null && (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)); } else { NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); return activeNetwork != null && activeNetwork.isConnectedOrConnecting(); } }
注意点:
- App进入后台后,系统可能会杀死进程,导致检测停止,所以只适合前台场景。
二、收到短信时自动发送邮件
这个需求需要完成短信监听和邮件发送两个核心部分,还要注意Android新版本的权限和后台限制:
步骤1:申请必要权限
在AndroidManifest.xml中添加:
<!-- 读取短信权限(危险权限,需动态申请) --> <uses-permission android:name="android.permission.READ_SMS" /> <!-- 接收广播权限 --> <uses-permission android:name="android.permission.RECEIVE_SMS" /> <!-- 网络权限,用于发送邮件 --> <uses-permission android:name="android.permission.INTERNET" /> <!-- Android 8+前台服务权限,保证后台能发送邮件 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
步骤2:动态注册短信广播接收器
Android 8.0之后,静态注册的SMS_RECEIVED广播会被系统拦截,必须动态注册:
public class MainActivity extends AppCompatActivity { private SmsReceiver mSmsReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 先申请读取短信权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_SMS}, 1001); } else { registerSmsListener(); } } private void registerSmsListener() { mSmsReceiver = new SmsReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("android.provider.Telephony.SMS_RECEIVED"); filter.setPriority(Integer.MAX_VALUE); // 确保优先收到短信广播 registerReceiver(mSmsReceiver, filter); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 1001) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { registerSmsListener(); } else { Toast.makeText(this, "需要读取短信权限才能实现功能", Toast.LENGTH_SHORT).show(); } } } @Override protected void onDestroy() { super.onDestroy(); // 注销广播,避免内存泄漏 if (mSmsReceiver != null) { unregisterReceiver(mSmsReceiver); } } }
步骤3:实现短信接收器和邮件发送逻辑
public class SmsReceiver extends BroadcastReceiver { private static final String TAG = "SmsReceiver"; @Override public void onReceive(Context context, Intent intent) { if ("android.provider.Telephony.SMS_RECEIVED".equals(intent.getAction())) { Bundle bundle = intent.getExtras(); if (bundle == null) return; Object[] pdus = (Object[]) bundle.get("pdus"); if (pdus == null) return; for (Object pdu : pdus) { SmsMessage sms = SmsMessage.createFromPdu((byte[]) pdu); String sender = sms.getDisplayOriginatingAddress(); String content = sms.getMessageBody(); Log.d(TAG, "收到短信:来自" + sender + ",内容:" + content); // 可选:添加过滤逻辑,比如只处理特定号码的短信 // if ("10086".equals(sender)) { ... } // 后台发送邮件,避免在广播接收器中做耗时操作 sendEmailInBackground(context, sender, content); } } } private void sendEmailInBackground(Context context, String sender, String content) { // 用Coroutine做后台任务(需要添加Coroutine依赖) CoroutineScope(Dispatchers.IO).launch { boolean success = sendEmail("你的邮箱地址", "收件人邮箱地址", "新短信通知", "发件人:" + sender + "\n短信内容:" + content); // 发送通知告知用户结果 showNotification(context, success ? "邮件发送成功" : "邮件发送失败", success ? "短信内容已发送至指定邮箱" : "请检查网络或邮箱配置"); } } // 邮件发送核心方法(用JavaMail实现) private boolean sendEmail(String fromEmail, String toEmail, String subject, String body) { try { Properties props = new Properties(); // 这里以QQ邮箱为例,其他邮箱修改对应的SMTP地址和端口 props.put("mail.smtp.host", "smtp.qq.com"); props.put("mail.smtp.port", "587"); props.put("mail.smtp.auth", "true"); props.put("mail.smtp.starttls.enable", "true"); Session session = Session.getInstance(props, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { // 注意:这里填邮箱的授权码,不是登录密码! return new PasswordAuthentication(fromEmail, "你的邮箱授权码"); } }); Message message = new MimeMessage(session); message.setFrom(new InternetAddress(fromEmail)); message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail)); message.setSubject(subject); message.setText(body); Transport.send(message); return true; } catch (Exception e) { e.printStackTrace(); return false; } } // 显示通知的工具方法 private void showNotification(Context context, String title, String content) { NotificationManagerCompat manager = NotificationManagerCompat.from(context); // Android 8+需要创建通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel("SMS_EMAIL_CHANNEL", "短信转邮件通知", NotificationManager.IMPORTANCE_DEFAULT); manager.createNotificationChannel(channel); } Notification notification = new NotificationCompat.Builder(context, "SMS_EMAIL_CHANNEL") .setSmallIcon(R.drawable.ic_notification) // 替换成你的通知图标 .setContentTitle(title) .setContentText(content) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .build(); manager.notify(1, notification); } }
步骤4:添加依赖(JavaMail和Coroutine)
在build.gradle(app)中添加:
android { packagingOptions { pickFirst 'META-INF/LICENSE.txt' pickFirst 'META-INF/NOTICE.txt' } } dependencies { // JavaMail依赖 implementation 'com.sun.mail:android-mail:1.6.7' implementation 'com.sun.mail:android-activation:1.6.7' // Coroutine依赖(用于后台任务) implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3' }
注意点:
- 邮箱授权码:需要在邮箱设置中开启POP3/SMTP服务,然后获取授权码(比如QQ邮箱、163邮箱都有这个选项)。
- Android后台限制:如果需要App在后台也能触发邮件发送,建议把邮件逻辑放到WorkManager中,避免广播接收器被系统限制。
- 短信过滤:建议添加过滤逻辑,只处理你需要的短信,避免不必要的邮件发送。
内容的提问来源于stack exchange,提问作者mohamadrezach




