这是一个非常经典但也很重要的话题,需要注意的是,Android 系统对后台网络服务有严格的限制,直接在主线程上运行 Socket 服务器是行不通的,并且即使放在后台,也容易被系统杀死,我们通常会结合前台服务来保证其稳定性。

下面我将提供一个完整的、可运行的示例,并解释其中的关键点和最佳实践。
核心概念
- Socket (套接字):是网络通信的端点,服务器端通过
ServerSocket在一个特定端口上“监听”客户端的连接请求,一旦有客户端连接,服务器就会创建一个新的Socket与该客户端进行一对一的通信。 - 线程:网络操作(如
accept(),read(),write())是阻塞的,如果这些操作在主线程(UI 线程)上执行,会导致应用界面卡死甚至崩溃,我们必须将这些操作放在子线程中。 - 前台服务:这是 Android 推荐的用于运行长时间运行的后台任务的服务,它会显示一个持续的通知,告诉用户这个应用正在执行一个重要的任务,从而降低了系统杀死它的概率,对于 Socket 这是一个很好的选择。
- 线程池:服务器需要能够同时处理多个客户端的连接,为每个客户端创建一个新线程是一种简单的方式,但如果客户端数量非常多,会导致资源耗尽,使用线程池(如
ExecutorService)可以更高效地管理线程。
完整示例代码
我们将创建一个应用,它启动一个前台服务,该服务内部运行一个 Socket 服务器,服务器可以接收客户端发来的消息,并将收到的消息通过广播发送给 Activity,以便在 UI 上显示。
添加网络权限
在 AndroidManifest.xml 中,必须声明网络权限。
<!-- AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.socketserver">
<!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 如果需要使用网络,Android 9 (API 28) 及以上版本还需要此权限 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application ...>
...
</application>
</manifest>
创建布局文件
用于显示服务器状态和接收到的消息。

<!-- activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/tv_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="服务器状态: 未启动"
android:textSize="18sp"
android:layout_marginBottom="16dp"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<TextView
android:id="@+id/tv_messages"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="等待接收消息..."
android:textSize="16sp" />
</ScrollView>
<Button
android:id="@+id/btn_start_server"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="启动服务器" />
<Button
android:id="@+id/btn_stop_server"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="停止服务器" />
</LinearLayout>
创建广播接收器
用于从服务接收消息并更新 UI。
// MessageReceiver.java
package com.example.socketserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.TextView;
public class MessageReceiver extends BroadcastReceiver {
private TextView tvMessages;
public MessageReceiver(TextView tvMessages) {
this.tvMessages = tvMessages;
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null && intent.getAction().equals(SocketService.ACTION_CLIENT_MESSAGE)) {
String message = intent.getStringExtra(SocketService.EXTRA_MESSAGE);
// 在UI线程上更新TextView
tvMessages.append("\n客户端: " + message);
}
}
}
创建 Socket 服务
这是核心部分,包含了服务器逻辑。
// SocketService.java
package com.example.socketserver;
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.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Enumeration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SocketService extends Service {
private static final String TAG = "SocketService";
private static final int PORT = 8888;
private static final String CHANNEL_ID = "SocketServiceChannel";
public static final String ACTION_CLIENT_MESSAGE = "com.example.socketserver.CLIENT_MESSAGE";
public static final String EXTRA_MESSAGE = "message";
private ServerSocket serverSocket;
private ExecutorService executorService;
private boolean isRunning = false;
@Override
public void onCreate() {
super.onCreate();
createNotificationChannel();
executorService = Executors.newCachedThreadPool();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(1, createNotification());
startServer();
return START_STICKY; // 如果服务被杀死,系统会尝试重新创建它
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null; // 我们不使用绑定服务
}
@Override
public void onDestroy() {
super.onDestroy();
stopServer();
}
private void startServer() {
if (isRunning) return;
isRunning = true;
executorService.execute(() -> {
try {
// 获取本机IP地址,方便客户端连接
String ipAddress = getLocalIpAddress();
Log.d(TAG, "服务器启动中... IP: " + ipAddress + ", 端口: " + PORT);
serverSocket = new ServerSocket(PORT);
Log.d(TAG, "服务器已启动,等待客户端连接...");
// 持续监听客户端连接
while (isRunning) {
try {
Socket clientSocket = serverSocket.accept();
Log.d(TAG, "客户端已连接: " + clientSocket.getInetAddress());
// 为每个客户端创建一个处理任务
ClientHandler clientHandler = new ClientHandler(clientSocket);
executorService.execute(clientHandler);
} catch (IOException e) {
if (isRunning) {
Log.e(TAG, "接受客户端连接时出错", e);
}
}
}
} catch (IOException e) {
Log.e(TAG, "启动服务器时出错", e);
}
});
}
private void stopServer() {
isRunning = false;
if (serverSocket != null && !serverSocket.isClosed()) {
try {
serverSocket.close();
Log.d(TAG, "服务器已停止");
} catch (IOException e) {
Log.e(TAG, "关闭服务器时出错", e);
}
}
if (executorService != null) {
executorService.shutdown();
}
stopForeground(true);
stopSelf();
}
// 处理单个客户端连接的内部类
private class ClientHandler implements Runnable {
private final Socket clientSocket;
public ClientHandler(Socket socket) {
this.clientSocket = socket;
}
@Override
public void run() {
try (
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)
) {
String inputLine;
while (isRunning && clientSocket.isConnected()) {
inputLine = in.readLine();
if (inputLine == null) {
break; // 客户端断开连接
}
Log.d(TAG, "收到消息: " + inputLine);
// 通过广播将消息发送给Activity
Intent intent = new Intent(ACTION_CLIENT_MESSAGE);
intent.putExtra(EXTRA_MESSAGE, inputLine);
sendBroadcast(intent);
// 简单的响应
out.println("服务器收到: " + inputLine);
}
} catch (IOException e) {
Log.e(TAG, "处理客户端时出错", e);
} finally {
try {
clientSocket.close();
} catch (IOException e) {
Log.e(TAG, "关闭客户端Socket时出错", e);
}
Log.d(TAG, "客户端连接已关闭");
}
}
}
// 获取本机IP地址
private String getLocalIpAddress() {
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces
