在 Button 點擊事件中建立 Socket 連線,雖然包裝在 Runnable 並呼叫 handler.post(runnable),仍拋出 NetworkOnMainThreadException。
Handler handler = new Handler();
handler.post(runnable); // ❌ 仍在主執行緒 (UI Thread) 執行!
Handler.post() 只是將 Runnable 放入主執行緒的訊息佇列,待主執行緒空閒時執行。本質上仍在主執行緒,無法解決網路操作限制。
new Thread(runnable).start(); // ✅ 開啟新執行緒執行
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(runnable); // 可復用執行緒、管理生命週期
// Kotlin
lifecycleScope.launch(Dispatchers.IO) {
val socket = Socket("172.16.5.30", 8000)
// ... 網路操作 ...
withContext(Dispatchers.Main) {
// 更新 UI
}
}
在子執行緒中不可直接操作 View(如 editText.setText())。需切回主執行緒:
runOnUiThread(() -> editText.setText(msg));
// 或
handler.post(() -> editText.setText(msg));
// 或 Kotlin
withContext(Dispatchers.Main) { editText.setText(msg) }
button.setOnClickListener(v -> {
new Thread(() -> {
try (Socket socket = new Socket("172.16.5.30", 8000);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
String message = editText.getText().toString() + "\r\n";
out.println(message);
String response = in.readLine();
runOnUiThread(() -> {
if (response != null) editText.setText(response);
else editText.setText("data error");
});
} catch (IOException e) {
e.printStackTrace();
runOnUiThread(() -> editText.setText("connection failed"));
}
}).start();
});
若目標伺服器非 HTTPS,需在 AndroidManifest.xml <application> 加入 android:usesCleartextTraffic="true",或配置 network_security_config.xml。
handler.post() ≠ 子執行緒,只是排程在主執行緒稍後執行。new Thread()、ExecutorService、Coroutine(Dispatchers.IO) 等真正背景執行緒執行。runOnUiThread() 或 Handler(Looper.getMainLooper()) 更新 UI。