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

Android TCP客户端无法向Python服务器发送Beacon RSSI数据求助

问题分析与解决方案

先直接戳中你的核心问题:你把网络等待、数据发送逻辑塞进了getView()方法里,而getView()是运行在UI线程的,这直接导致UI线程被阻塞,不仅会引发ANR(应用无响应),还让你的数据发送逻辑彻底混乱。

核心问题拆解

  • UI线程阻塞:getView()里的while(serveractivate == 0)循环会死死卡住UI线程——UI线程被用来等待Socket连接,完全违背Android线程规范,后续发送逻辑自然无法正常执行,甚至整个界面都会卡死。
  • 逻辑位置错误:数据收集、Socket发送这类业务逻辑绝对不能放在getView()中,因为系统会在列表滚动、数据更新时频繁调用getView(),这会导致你的连接、拼接、发送逻辑被重复触发,出现不可控的混乱。
  • 线程安全隐患:serveractivate、stackcnt这些变量没有做线程同步,在UI线程和Socket线程之间交叉访问,很容易出现状态判断错误。

修复方案

1. 重构Socket与数据发送逻辑

把所有网络操作移到独立后台线程,绝对不能占用UI线程。同时将Beacon数据收集、发送逻辑从Adapter中剥离,放到MainActivity或专门的业务管理类中。

2. 简化Adapter职责

Adapter只负责展示数据,不处理任何业务逻辑(网络、数据收集),彻底避免getView()频繁调用带来的逻辑混乱。

3. 代码修改示例

第一步:重构MainActivity,新增数据发送管理类

public class MainActivity extends Activity implements BeaconCallback, Runnable {
    // ... 原有代码保持不变 ...
    private BeaconDataSender beaconDataSender; // 新增:专门的发送器

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // ... 原有代码 ...
        beaconDataSender = new BeaconDataSender(); // 初始化发送器
        beaconDataSender.connectToServer(); // 启动后台连接
    }

    @Override
    public void onBeaconCallback(int status, Beacon beacon) {
        switch (status) {
            case STATUS_CODE_ENTER:
            case STATUS_CODE_UPDATE:
                if (adapter != null) adapter.addBeacon(beacon);
                // 新增:把Beacon数据传给发送器
                beaconDataSender.addBeaconData(beacon.getRssi());
                break;
            case STATUS_CODE_EXIT:
                if (adapter != null) adapter.removeBeacon(beacon);
                // 新增:移除对应数据(可选)
                beaconDataSender.removeBeaconData(beacon.getMac());
                break;
            default:
                break;
        }
        runOnUiThread(this);
    }

    // ... 原有代码 ...

    @Override
    protected void onPause() {
        super.onPause();
        contextManager.stopLeScan();
        beaconDataSender.stopSocket(); // 新增:断开连接
    }

    // 新增:专门负责Beacon数据发送的内部类
    private class BeaconDataSender {
        private Socket socket = null;
        private PrintWriter out = null;
        private final int PORT = 9999;
        private final String SERVER_IP = "192.168.0.4";
        private final int TARGET_BEACON_COUNT = 2;
        private final List<Integer> rssiList = Collections.synchronizedList(new ArrayList<>());
        private boolean isConnected = false;

        // 后台连接服务器
        public void connectToServer() {
            new Thread(() -> {
                try {
                    socket = new Socket(SERVER_IP, PORT);
                    socket.setTcpNoDelay(true);
                    out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
                    isConnected = true;
                    Log.w("state ", "server connected");
                } catch (IOException e) {
                    Log.w("state ", "failed to connect server");
                    e.printStackTrace();
                }
            }).start();
        }

        // 添加Beacon RSSI数据
        public void addBeaconData(int rssi) {
            if (rssiList.size() >= TARGET_BEACON_COUNT) {
                rssiList.clear();
            }
            rssiList.add(rssi);
            // 当收集到目标数量的数据时,发送
            if (rssiList.size() == TARGET_BEACON_COUNT && isConnected) {
                sendRssiData();
            }
        }

        // 移除指定Beacon的数据(可选)
        public void removeBeaconData(String mac) {
            // 如果需要根据MAC跟踪数据,这里可以调整逻辑
            if (!rssiList.isEmpty()) {
                rssiList.remove(0);
            }
        }

        // 发送数据到服务器
        private void sendRssiData() {
            if (out == null || !isConnected) return;
            new Thread(() -> {
                StringBuilder sb = new StringBuilder();
                for (int rssi : rssiList) {
                    sb.append(rssi).append(" ");
                }
                String sendStr = sb.toString().trim();
                try {
                    out.println(sendStr);
                    out.flush();
                    Log.d("sendrssi ", sendStr);
                    rssiList.clear(); // 发送后清空列表
                } catch (Exception e) {
                    e.printStackTrace();
                    isConnected = false;
                    // 可选:尝试重连
                    connectToServer();
                }
            }).start();
        }

        // 断开连接
        public void stopSocket() {
            try {
                if (out != null) {
                    out.close();
                }
                if (socket != null && !socket.isClosed()) {
                    socket.close();
                }
                isConnected = false;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

第二步:简化BeaconListAdapter,只负责展示

public class BeaconListAdapter extends BaseAdapter {
    private final LayoutInflater inflater;
    private int count;
    private final ArrayMap<String, Beacon> itemMap = new ArrayMap<>();
    private final int padding;

    public BeaconListAdapter(Context context) {
        super();
        padding = (int) context.getResources().getDimension(R.dimen.activity_vertical_margin);
        this.inflater = LayoutInflater.from(context);
    }

    public int addBeacon(Beacon beacon) {
        synchronized (itemMap) {
            itemMap.put(beacon.getMac(), beacon);
            count = itemMap.size();
            return count;
        }
    }

    public int removeBeacon(Beacon beacon) {
        synchronized (itemMap) {
            itemMap.remove(beacon.getMac());
            count = itemMap.size();
            return count;
        }
    }

    @Override
    public int getCount() {
        return count;
    }

    @Override
    public Beacon getItem(int position) {
        synchronized (itemMap) {
            return itemMap.valueAt(position);
        }
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        TextView textView = null;
        if (convertView == null) {
            convertView = inflater.inflate(android.R.layout.simple_list_item_1, parent, false);
            textView = (TextView) convertView.findViewById(android.R.id.text1);
            textView.setBackgroundColor(Color.WHITE);
            textView.setTextColor(Color.BLACK);
            textView.setPadding(padding, padding, padding, padding);
            convertView.setTag(textView);
        } else {
            textView = (TextView) convertView.getTag();
        }
        Beacon item = getItem(position);
        int temprssi = item.getRssi();
        int[] values = BeaconUtils.getAccelerometer(item);
        
        // 只负责展示数据,去掉所有网络和发送逻辑
        textView.setText(String.format("[%s]\nMAC : %s\nRssi : %d\n", 
                item.getType() == Beacon.TYPE_MIDAS ? "Midascon" : "Beacon", 
                item.getMac(), temprssi));

        return convertView;
    }
}

关键优化点说明

  • 线程隔离:所有网络操作(连接、发送)都放在后台线程,UI线程只负责界面更新和数据收集通知,彻底避免阻塞。
  • 职责分离:Adapter专注UI展示,数据收集和发送交给专门类处理,代码结构更清晰,逻辑更可控。
  • 线程安全:用Collections.synchronizedList保证RSSI列表的线程安全,避免多线程访问异常。
  • 稳定可靠:移除了getView()中的死循环,界面流畅度和稳定性大幅提升。

内容的提问来源于stack exchange,提问作者jason

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 20:47:54