You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 16:00:36