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

为何部分Android设备无法按10Hz频率接收蓝牙GPS模块的NMEA数据?

蓝牙GPS模块NMEA数据接收卡顿问题及解决求助

我正在开发一款解析10Hz蓝牙GPS模块NMEA数据的App,遇到如下问题:部分Android设备/版本接收蓝牙NMEA数据的间隔并非10Hz模块应有的约100ms,而是200-300ms,且会一次性收到多个时间戳的数据。这对后处理无影响,但会导致实时界面(如TextView)更新卡顿。

异常示例:蓝牙数据接收卡顿

以下是readLine()函数输出的控制台日志,对比控制台输出时间与GPS UTC时间可看出异常:

Console Output Time     GPS Time
13:35:11.555            NMEA UTC  123618.277
13:35:11.555            NMEA UTC  123618.377
13:35:11.805            NMEA UTC  123618.477 <--- 在TextView中无法显示
13:35:11.805            NMEA UTC  123618.577 <--- 在TextView中仅显示5ms
13:35:11.810            NMEA UTC  123618.677 <--- 因后续长停顿(245ms),该值会在TextView中显示
13:35:12.055            NMEA UTC  123618.777 <--- 无法显示
13:35:12.055            NMEA UTC  123618.877 <--- 显示250ms
13:35:12.305            NMEA UTC  123618.977

【异常情况界面演示:TextView更新卡顿,数据跳变明显,无法连续显示所有时间戳】

可见控制台时间如13:35:11.805出现两次,紧接着是13:35:11.810,这意味着TextView只会显示最后一行123618.677,随后长时间停顿后显示123618.877。readLine()会快速调用2-3次,然后停顿200-300ms,循环往复,导致TextView更新卡顿。

正常示例:蓝牙数据均匀接收

以下是正常接收的情况:

Console Output Time     GPS Time
13:42:37.229            NMEA UTC  124239.073
13:42:37.335            NMEA UTC  124239.173
13:42:37.417            NMEA UTC  124239.273 <---
13:42:37.522            NMEA UTC  124239.373 <---
13:42:37.632            NMEA UTC  124239.473 <--- 所有NMEA语句接收间隔约100ms,TextView更新均匀
13:42:37.719            NMEA UTC  124239.573 <--- 
13:42:37.826            NMEA UTC  124239.673 <---
13:42:37.932            NMEA UTC  124239.773
13:42:38.013            NMEA UTC  124239.873
13:42:38.118            NMEA UTC  124239.973

【正常情况界面演示:TextView更新均匀流畅,每约100ms刷新一次时间戳】

此情况下数据约每100ms接收一次,TextView更新流畅均匀。

设备表现差异

该正常情况在Galaxy S Plus(Android 4.3.1)上始终出现,但在Galaxy S3(Android 4.3)和Huawei P40 Pro(Android 10)上则出现异常。有趣的是,在Huawei P40 Pro上快速断开并重新连接App后,常能切换到稳定快速传输,但Galaxy S3上很少出现此情况;通过App重新配对蓝牙设备(输入PIN码)有时也能恢复正常,但多数情况下重新连接后又回到异常状态。测试其他蓝牙GPS App也存在相同的传输频率不稳定问题。是否有人遇到过相同情况?该如何解决?

代码示例

以下代码会导致上述异常控制台输出,需替换GPS模块的MAC地址,并添加包含txtTime TextView的activity_logging_extern_gps_layout_test.xml文件:

package your.packages.activities;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.TextView;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import your.packages.R;

public class LiveViewBluetoothGPSActivityTEST extends Activity
{
    public static final int MESSAGE_READ = 2;
    private TextView txtTime;
    private BluetoothAdapter mBluetoothAdapter = null;
    private BluetoothConnectionService mBluetConServ = null;

    private final Handler mHandler = new Handler(Looper.getMainLooper())
    {
        @Override
        public void handleMessage(Message msg)
        {
            switch(msg.what)
            {
                case MESSAGE_READ:
                    String readMessage = (String) msg.obj;

                    if(readMessage.startsWith("$GPGGA"))
                    {
                        Log.d("NMEA UTC", readMessage);
                        String timestamp = readMessage.split(",")[1];
                        txtTime.setText("Time: " + timestamp);
                    }
                    break;
            }
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_logging_extern_gps_layout_test);
        txtTime = (TextView) findViewById(R.id.txtTime);
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    }

    @Override
    public void onStart()
    {
        super.onStart();

        mBluetConServ = new BluetoothConnectionService(this, mHandler);
    }

    @Override
    public synchronized void onResume()
    {
        super.onResume();

        mBluetConServ.start();
        String deviceAddress = "00:11:22:33:44:55"; // put the mac address of your GPS modul here
        BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(deviceAddress);
        mBluetConServ.connect(device);
    }
}


class BluetoothConnectionService
{
    private static final String TAG = "BluetoothConnectionServ";
    private final BluetoothAdapter mAdapter;
    private final Handler mHandler;
    private ConnectThread mConnectThread;
    private ConnectedThread mConnectedThread;

    public BluetoothConnectionService(Context context, Handler handler)
    {
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mHandler = handler;
    }

    public synchronized void start()
    {
        if(mConnectThread != null)
        {
            mConnectThread.cancel();
            mConnectThread = null;
        }

        if(mConnectedThread != null)
        {
            mConnectedThread.cancel();
            mConnectedThread = null;
        }

    }

    public synchronized void connect(BluetoothDevice device)
    {
        if(mConnectedThread != null)
        {
            mConnectedThread.cancel();
            mConnectedThread = null;
        }

        mConnectThread = new ConnectThread(device);
        mConnectThread.start();
    }

    public synchronized void connected(BluetoothSocket socket, BluetoothDevice device)
    {
        mConnectedThread = new ConnectedThread(socket);
        mConnectedThread.start();
    }

    private class ConnectThread extends Thread
    {
        private final BluetoothSocket mmSocket;
        private final BluetoothDevice mmDevice;

        public ConnectThread(BluetoothDevice device)
        {
            mmDevice = device;
            BluetoothSocket tmp = null;

            try
            {
                Method m = device.getClass().getMethod("createRfcommSocket", int.class);
                tmp = (BluetoothSocket) m.invoke(device, 1);

            } catch(InvocationTargetException e)
            {
                throw new RuntimeException(e);
            } catch(NoSuchMethodException e)
            {
                throw new RuntimeException(e);
            } catch(IllegalAccessException e)
            {
                throw new RuntimeException(e);
            }
            mmSocket = tmp;
        }

        public void run()
        {
            setName("ConnectThread");

            mAdapter.cancelDiscovery();

            try
            {
                mmSocket.connect();
            } catch(IOException e)
            {
                try
                {
                    mmSocket.close();
                } catch(IOException e2)
                {
                    Log.e(TAG, "unable to close() socket during connection failure", e2);
                }
                BluetoothConnectionService.this.start();
                return;
            }

            synchronized(BluetoothConnectionService.this)
            {
                mConnectThread = null;
            }

            connected(mmSocket, mmDevice);
        }

        public void cancel()
        {
            try
            {
                mmSocket.close();
            } catch(IOException e)
            {
                Log.e(TAG, "close() of connect socket failed", e);
            }
        }
    }

    private class ConnectedThread extends Thread
    {
        private final BluetoothSocket mmSocket;
        private final BufferedReader mmBuffReader;

        public ConnectedThread(BluetoothSocket socket)
        {
            mmSocket = socket;
            InputStream tmpIn = null;

            try
            {
                tmpIn = socket.getInputStream();
            } catch(IOException e)
            {
                Log.e(TAG, "temp sockets not created", e);
            }

            mmBuffReader = new BufferedReader(new InputStreamReader(tmpIn));
        }

        public void run()
        {
            String line;

            while(true)
            {
                try
                {
                    if((line = mmBuffReader.readLine()) != null)
                        mHandler.obtainMessage(LiveViewBluetoothGPSActivityTEST.MESSAGE_READ, line).sendToTarget();

                } catch(IOException e)
                {
                    break;
                }
            }
        }

        public void cancel()
        {
            try
            {
                mmSocket.close();
            } catch(IOException e)
            {
                Log.e(TAG, "close() of connect socket failed", e);
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 10:10:33