Android ZXing扫码后无法向TCP Server发送数据问题求助
问题诊断与修复方案
核心崩溃原因
- 子线程执行UI操作:
AsyncTask的doInBackground()运行在后台线程,Android禁止在非UI线程直接调用Toast(属于UI操作),这会触发CalledFromWrongThreadException直接导致应用崩溃。 - 异常处理逻辑违规:catch块中的
Toast同样在后台线程执行,会进一步引发崩溃。
其他潜在问题
- 缺失网络权限:未在
AndroidManifest.xml中声明INTERNET权限,应用无法发起TCP连接。 - 资源未安全释放:Socket、DataOutputStream使用后未确保关闭,易造成资源泄漏。
- 输入合法性未校验:
HOST_ADDR.getText()可能为空,直接转换为字符串会导致连接无效的"null"地址。
修复后的代码
1. 修正BarcodeSender类
将UI操作移至onPostExecute()(运行在UI线程),完善资源关闭逻辑,用日志输出异常信息:
import android.content.Context; import android.os.AsyncTask; import android.util.Log; import android.widget.Toast; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; public class BarcodeSender extends AsyncTask<String, Void, Boolean> { private static final String TAG = "BarcodeSender"; private final String adr; private final int port; private final Context context; private String errorMsg; public BarcodeSender(Context context, String address, int PORT) { this.adr = address; this.port = PORT; this.context = context; Toast.makeText(this.context, adr + ":" + port, Toast.LENGTH_SHORT).show(); } @Override protected Boolean doInBackground(String... voids) { String mess = voids[0]; Socket socket = null; DataOutputStream dos = null; try { // 校验服务器地址合法性 if (adr == null || adr.trim().isEmpty()) { errorMsg = "服务器地址不能为空"; return false; } socket = new Socket(this.adr, this.port); dos = new DataOutputStream(socket.getOutputStream()); dos.writeUTF(mess); dos.flush(); // 确保数据完全发送 return true; } catch (IOException e) { Log.e(TAG, "网络连接失败", e); errorMsg = "网络错误: " + e.getMessage(); return false; } catch (Exception exs) { Log.e(TAG, "未知错误", exs); errorMsg = "发送失败: " + exs.getMessage(); return false; } finally { // 安全关闭资源 try { if (dos != null) dos.close(); if (socket != null) socket.close(); } catch (IOException e) { Log.e(TAG, "资源关闭失败", e); } } } @Override protected void onPostExecute(Boolean success) { super.onPostExecute(success); if (success) { Toast.makeText(context, "条码数据发送成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context, errorMsg, Toast.LENGTH_LONG).show(); } } }
2. 修正调用处代码
添加地址空值校验,避免无效连接:
private ActivityResultLauncher<ScanOptions> barcodeLauncher = registerForActivityResult(new ScanContract(), result -> { try { if (result.getContents() != null) { String data = result.getContents(); String hostAddr = String.valueOf(HOST_ADDR.getText()).trim(); if (hostAddr.isEmpty()) { Toast.makeText(getApplicationContext(), "请输入服务器地址", Toast.LENGTH_SHORT).show(); return; } new BarcodeSender(getApplicationContext(), hostAddr, 3946).execute(data); } } catch (Exception ex) { Toast.makeText(getApplicationContext(), "扫码错误: " + ex.toString(), Toast.LENGTH_LONG).show(); } statusText.setTextColor(Color.rgb(255, 0, 0)); });
3. 添加网络权限
在AndroidManifest.xml的<manifest>标签内添加:
<uses-permission android:name="android.permission.INTERNET"/>
额外优化建议
AsyncTask在Android 11及以上已被弃用,推荐使用协程替代,示例代码:
// 需先添加Coroutine依赖 private fun sendBarcodeData(data: String, host: String, port: Int) { CoroutineScope(Dispatchers.IO).launch { var socket: Socket? = null var dos: DataOutputStream? = null try { socket = Socket(host, port) dos = DataOutputStream(socket.getOutputStream()) dos.writeUTF(data) dos.flush() withContext(Dispatchers.Main) { Toast.makeText(applicationContext, "发送成功", Toast.LENGTH_SHORT).show() } } catch (e: Exception) { Log.e("BarcodeSender", "发送失败", e) withContext(Dispatchers.Main) { Toast.makeText(applicationContext, "发送失败: ${e.message}", Toast.LENGTH_LONG).show() } } finally { dos?.close() socket?.close() } } }
内容的提问来源于stack exchange,提问作者CrazyProgrammer
相关产品推荐
相关产品推荐

