开机后后台启动服务连接服务器超时问题排查
问题分析与解决方案
我一眼就看到了核心问题——你在Service的onCreate()里调用了AsyncTask.execute().get()!这个get()方法是同步阻塞调用,它会让当前线程(也就是Service的主线程)一直等待AsyncTask完成后台任务才会继续执行。这不仅会导致Service卡死无法正常初始化,甚至会触发ANR(应用无响应),你的监听逻辑根本没机会正常跑起来。
下面是具体的问题拆解和修复方案:
1. 移除阻塞的get()调用,让AsyncTask真正异步执行
Service的onCreate()运行在主线程,绝对不能在这里做阻塞操作。你需要直接执行AsyncTask,不要用get()等待结果,而是通过onProgressUpdate()或者回调来处理收到的消息。
2. 修复AsyncTask中的监听逻辑bug
你的connectAndListen()方法里有个低级错误:
while ((message = in.readLine()) != null){ message = in.readLine(); // 这里又读了一次,会跳过第一行数据! break; }
第一次readLine()已经拿到了消息,第二次调用会直接读取下一行(如果有的话),导致你丢失了实际收到的内容。
3. 适配Android版本限制
- BOOT_COMPLETED广播限制:Android 3.1及以上版本,应用安装后必须被用户手动启动过一次,系统才会发送开机广播给它。如果你的应用从来没被打开过,BootReceiver根本不会触发。
- 前台服务要求:Android 8.0(API 26)及以上,后台服务会被系统限制,必须启动为前台服务并显示一个低优先级通知,否则Service会被系统杀死。
修正后的代码示例
修正后的AppService
import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Service; import android.content.Intent; import android.os.Build; import android.os.IBinder; import android.widget.Toast; import androidx.core.app.NotificationCompat; public class AppService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); // 适配Android O及以上的前台服务要求 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( "app_listener_channel", "后台消息监听", NotificationManager.IMPORTANCE_LOW ); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); Notification notification = new NotificationCompat.Builder(this, "app_listener_channel") .setSmallIcon(R.drawable.ic_launcher_foreground) // 替换成你的应用图标 .setContentTitle("消息监听服务") .setContentText("正在监听服务器消息") .build(); startForeground(1, notification); } Toast.makeText(this, "In service app", Toast.LENGTH_LONG).show(); // 直接启动AsyncTask,不要调用get() new ListenForUpdates(this).execute(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { super.onStartCommand(intent, flags, startId); return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); } }
修正后的ListenForUpdates AsyncTask
import android.content.Context; import android.os.AsyncTask; import android.widget.Toast; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; import java.lang.ref.WeakReference; public class ListenForUpdates extends AsyncTask<Void, String, Void> { private static final int portNumber = 12302; private ServerSocket serverSocket; private Socket socket; private BufferedReader in; private WeakReference<Context> contextRef; // 使用弱引用避免内存泄漏 public ListenForUpdates(Context context) { contextRef = new WeakReference<>(context); } @Override protected Void doInBackground(Void... voids) { try { serverSocket = new ServerSocket(portNumber); // 循环监听,直到任务被取消 while (!isCancelled()) { socket = serverSocket.accept(); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); String message; // 修复重复readLine()的问题 while ((message = in.readLine()) != null) { publishProgress(message); // 将消息传递到主线程显示 break; // 如果只需要处理一条消息就断开连接,否则移除break持续监听 } // 关闭当前连接的资源 in.close(); socket.close(); } } catch (IOException e) { e.printStackTrace(); } return null; } // 在主线程处理收到的消息 @Override protected void onProgressUpdate(String... values) { super.onProgressUpdate(values); Context context = contextRef.get(); if (context != null) { Toast.makeText(context, "收到消息:" + values[0], Toast.LENGTH_LONG).show(); } } // 任务取消时清理资源 @Override protected void onCancelled() { super.onCancelled(); try { if (in != null) in.close(); if (socket != null) socket.close(); if (serverSocket != null) serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }
额外注意事项
- 服务器端连接:如果是真机测试,不要用
10.0.2.2(这是模拟器指向主机的特殊IP),需要改用你的服务器所在的实际IP,并且确保手机和服务器在同一局域网,防火墙没有拦截12302端口。 - BootReceiver触发条件:安装应用后,一定要手动打开一次应用,否则开机广播不会被触发。如果应用被用户强制停止,也会收不到开机广播。
内容的提问来源于stack exchange,提问作者sm87
相关产品推荐
相关产品推荐

