Android Studio Java实现锁屏解锁后自动后台Socket连接并推送通知问题
问题修复方案
现有代码核心问题
- 广播接收器没有注册
ACTION_USER_PRESENT动作,无法接收手机解锁的系统广播 - 错误将
UserPresentBroadcastReceiver同时注册为广播和服务,存在类冲突 - 在广播的
onReceive方法中执行死循环、网络请求等耗时操作,会直接触发ANR(应用无响应),被系统强制杀死进程 - 网络请求直接运行在主线程,会抛出主线程网络异常
- Android 12及以上版本PendingIntent未指定可变性flag,会抛出异常
修复步骤
1. 修正AndroidManifest.xml配置
删掉错误的服务注册,新增解锁广播监听、前台服务权限,调整后的完整代码如下:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.example.notification"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <!-- Android 8.0+前台服务所需权限 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Notification"> <receiver android:name=".UserPresentBroadcastReceiver" android:exported="true"> <intent-filter> <category android:name="android.intent.category.DEFAULT"/> <action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.QUICKBOOT_POWERON"/> <!--HTC设备开机广播兼容--> <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/> <!--新增解锁广播监听动作--> <action android:name="android.intent.action.USER_PRESENT" /> </intent-filter> </receiver> <!--新增独立的消息检查服务--> <service android:name=".MessageCheckService" android:exported="false"/> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
2. 简化广播接收器逻辑
广播仅负责触发服务启动,不执行任何耗时操作:
public class UserPresentBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if(Intent.ACTION_USER_PRESENT.equals(action) || Intent.ACTION_BOOT_COMPLETED.equals(action)){ Intent serviceIntent = new Intent(context, MessageCheckService.class); // Android 8.0+需要用startForegroundService启动前台服务 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { context.startForegroundService(serviceIntent); } else { context.startService(serviceIntent); } } else if (Intent.ACTION_SHUTDOWN.equals(action)) { // 关机时停止服务 context.stopService(new Intent(context, MessageCheckService.class)); } } }
3. 新增消息检查前台服务
将Socket请求、循环检查、通知逻辑都放到服务的子线程执行:
import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.IBinder; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; public class MessageCheckService extends Service { private Thread checkThread; private volatile boolean isRunning = false; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); isRunning = true; // Android 8.0+前台服务必须展示运行通知 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel serviceChannel = new NotificationChannel( "SERVICE_RUN_CHANNEL", "后台运行通知", NotificationManager.IMPORTANCE_LOW ); getSystemService(NotificationManager.class).createNotificationChannel(serviceChannel); Notification runNotification = new NotificationCompat.Builder(this, "SERVICE_RUN_CHANNEL") .setContentTitle("消息检查服务运行中") .setSmallIcon(R.mipmap.ic_launcher) .build(); startForeground(1, runNotification); } // 子线程执行循环检查逻辑 checkThread = new Thread(() -> { while (isRunning) { Socket client = null; PrintWriter printWriter = null; BufferedReader bufferedReader = null; String newMessage = null; try { client = new Socket("192.168.1.13", 1580); printWriter = new PrintWriter(client.getOutputStream(), true); printWriter.write("<meet>/notification/"); printWriter.flush(); bufferedReader = new BufferedReader(new InputStreamReader(client.getInputStream())); newMessage = bufferedReader.readLine(); } catch (Exception e) { e.printStackTrace(); } finally { // 资源释放 try { if (client != null) client.close(); if (printWriter != null) printWriter.close(); if (bufferedReader != null) bufferedReader.close(); } catch (Exception e) { e.printStackTrace(); } } // 收到有效消息再推送通知 if (newMessage != null && !newMessage.trim().isEmpty()) { pushNotification(newMessage); } // 间隔5秒检查 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); break; } } }); checkThread.start(); } private void pushNotification(String content) { NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); String msgChannelId = "MSG_NOTIFY_CHANNEL"; // 适配Android 8.0+通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel msgChannel = new NotificationChannel( msgChannelId, "新消息通知", NotificationManager.IMPORTANCE_DEFAULT ); msgChannel.setDescription("收到新消息时推送通知"); notificationManager.createNotificationChannel(msgChannel); } // 适配Android 12+ PendingIntent可变性要求 int pendingFlag = PendingIntent.FLAG_UPDATE_CURRENT; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { pendingFlag |= PendingIntent.FLAG_IMMUTABLE; } PendingIntent clickIntent = PendingIntent.getActivity( this, 0, new Intent(this, MainActivity.class), pendingFlag ); Notification notification = new NotificationCompat.Builder(this, msgChannelId) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("新消息") .setContentText(content) .setAutoCancel(true) .setContentIntent(clickIntent) .build(); // 用时间戳作为通知id避免覆盖历史通知 notificationManager.notify((int) System.currentTimeMillis(), notification); } @Override public void onDestroy() { super.onDestroy(); isRunning = false; if (checkThread != null) { checkThread.interrupt(); } stopForeground(true); } }
注意事项
- 应用首次安装后必须手动打开一次,否则Android系统的安全机制会禁止静态广播触发
- 国内厂商ROM普遍有后台省电策略,需要手动给应用开启自启动权限、加入后台运行白名单,否则进程被杀死后无法接收广播
- 如果不需要每5秒循环检查,仅需要每次解锁时检查一次,可以去掉服务内的
while循环,检查完成后直接调用stopSelf()停止服务即可
内容的提问来源于stack exchange,提问作者raphael
相关产品推荐
相关产品推荐

