如何让蓝牙 RFCOMM 始终如一地工作?
Posted
技术标签:
【中文标题】如何让蓝牙 RFCOMM 始终如一地工作?【英文标题】:How to get bluetooth RFCOMM to work consistently? 【发布时间】:2011-04-04 19:58:06 【问题描述】:我正在尝试构建一个 android 应用程序,该应用程序将通过蓝牙串行端口配置文件 (SPP) 与外部 GPS 接收器连接。我正在使用运行 2.3.3 的 Nexus One。我已经设法让我的应用程序从 GPS 接收数据,但我有两个问题:1)当我连接到设备时,它只在某些时候有效。有时连接只是超时,有时它说设备正忙或正在使用中。 2) 我无法弄清楚如何将数据发送回设备,这可能是我如何使用流的问题,因为传入的流是阻塞调用。
我只是将相关代码移到了一个新的 Android 应用程序中进行测试,如下所示:
/res/layout/main.xml(两个按钮和一个文本视图)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_ android:layout_>
<Button android:id="@+id/btnStart" android:layout_ android:layout_ android:text="Connect"></Button>
<Button android:id="@+id/btnSend" android:layout_ android:layout_ android:text="Send Message"></Button>
<TextView android:id="@+id/textStatus" android:textSize="24sp" android:layout_ android:layout_ android:text="Status Goes Here" />
</LinearLayout>
/src/com.example.bluetoothspp/MainActivity.java
package com.example.bluetoothspp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.SocketTimeoutException;
import java.util.UUID;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity
private static final String TAG = "MainActivity";
private static final String BTAG = "BTThread";
static final int MSG_BT_GOT_DATA = 1;
static final int MSG_BT_STATUS_MSG = 2;
static final int MSG_BT_FINISHED = 99;
Button btnStart, btnSend;
TextView textStatus;
private BluetoothAdapter mBluetoothAdapter = null;
private BluetoothDevice btdevice = null;
Thread bThread;
BluetoothSocket bsocket;
InputStream bis = null; //Bluetooth input stream
OutputStream bos = null; //Bluetooth output stream
private String MACAddress = "00:01:95:06:1F:32";
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnStart = (Button)findViewById(R.id.btnStart);
btnSend = (Button)findViewById(R.id.btnSend);
textStatus = (TextView)findViewById(R.id.textStatus);
btnStart.setOnClickListener(btnStartListener);
btnSend.setOnClickListener(btnSendListener);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
private OnClickListener btnStartListener = new OnClickListener()
public void onClick(View v)
if(btnStart.getText().equals("Connect"))
Log.i(TAG, "Connect button pressed");
if (mBluetoothAdapter == null) //No adapter. Fail
Log.e(TAG, "getDefaultAdapter returned null");
textStatus.setText("getDefaultAdapter returned null");
else
if (!mBluetoothAdapter.isEnabled()) //Bluetooth disabled
Log.e(TAG, "Bluetooth is Disabled");
textStatus.setText("Bluetooth is Disabled");
else
Log.i(TAG, "Connecting to Device: " + MACAddress);
btdevice = mBluetoothAdapter.getRemoteDevice(MACAddress);
Log.i(TAG, "Device: " + btdevice.getName());
Log.i(TAG, "Trying to Connect...");
textStatus.setText("Trying to Connect...");
Log.i(TAG, "Starting Thread");
try
bThread = new Thread(new BluetoothClient(btdevice, true));
bThread.start();
catch (IOException e)
Log.e(TAG, "Could not create thread for bluetooth: " + e);
textStatus.setText("Could not create thread for bluetooth...");
btnStart.setText("Disconnect");
else
Log.i(TAG, "Disconnect button pressed");
btnStart.setText("Connect");
;
private OnClickListener btnSendListener = new OnClickListener()
public void onClick(View v)
textStatus.setText("Sending Message to Thread.");
SendDataToBluetooth("something\r\n");
;
public class BluetoothClient implements Runnable
public BluetoothClient(BluetoothDevice device, boolean IsAnHTCDevice) throws IOException
if (IsAnHTCDevice)
//This is a workaround for HTC devices, but it likes to throw an IOException "Connection timed out"
try
Method m = device.getClass().getMethod("createRfcommSocket", new Class[] int.class);
bsocket = (BluetoothSocket) m.invoke(device, Integer.valueOf(1));
catch (Exception e)
Log.e(BTAG, "Error at HTC/createRfcommSocket: " + e);
e.printStackTrace();
handler.sendMessage(handler.obtainMessage(MSG_BT_STATUS_MSG, "MethodException: " + e));
else
//This is the normal method, but on a Nexus One it almost always throws an IOException "Service discovery failed" message
try
UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
bsocket = device.createRfcommSocketToServiceRecord(MY_UUID);
catch (Exception e)
Log.e(BTAG, "Error at createRfcommSocketToServiceRecord: " + e);
e.printStackTrace();
handler.sendMessage(handler.obtainMessage(MSG_BT_STATUS_MSG, "MethodException: " + e));
public void run()
try
Log.i(BTAG, "Cancelling Discovery");
mBluetoothAdapter.cancelDiscovery();
Log.i(BTAG, "Connecting to Socket");
bsocket.connect();
bis = bsocket.getInputStream();
bos = bsocket.getOutputStream();
Log.i(BTAG, "Socket created, streams assigned");
handler.sendMessage(handler.obtainMessage(MSG_BT_STATUS_MSG, "Device Connected"));
Log.i(BTAG, "Waiting for data...");
byte[] buffer = new byte[4096];
int read = bis.read(buffer, 0, 4096); // This is blocking
Log.i(BTAG, "Getting data...");
while (read != -1)
byte[] tempdata = new byte[read];
System.arraycopy(buffer, 0, tempdata, 0, read);
handler.sendMessage(handler.obtainMessage(MSG_BT_GOT_DATA, tempdata));
read = bis.read(buffer, 0, 4096); // This is blocking
catch (SocketTimeoutException e)
e.printStackTrace();
catch (IOException e)
e.printStackTrace();
catch (Exception e)
e.printStackTrace();
finally
Log.i(BTAG, "Finished");
handler.sendMessage(handler.obtainMessage(MSG_BT_FINISHED));
public void SendDataToBluetooth(String cmd) // You run this from the main thread.
try
if (bsocket != null)
bos.write(cmd.getBytes());
catch (Exception e)
Log.e("SendDataToBluetooth", "Message send failed. Caught an exception: " + e);
public Handler handler = new Handler() // Handler for data coming from the network and bluetooth sockets
@Override
public void handleMessage(Message msg)
switch (msg.what)
case MSG_BT_GOT_DATA:
Log.i("handleMessage", "MSG_BT_GOT_DATA: " + (String) msg.obj);
textStatus.setText((String) msg.obj);
break;
case MSG_BT_STATUS_MSG:
Log.i("handleMessage", "MSG_BT_STATUS_MSG: " + (String) msg.obj);
textStatus.setText((String) msg.obj);
break;
case MSG_BT_FINISHED:
Log.i("handleMessage", "MSG_BT_FINISHED");
btnStart.setText("Connect");
break;
default:
super.handleMessage(msg);
;
@Override
protected void onDestroy()
super.onDestroy();
if (bThread != null) // If the thread is currently running, close the socket and interrupt it.
Log.i(BTAG, "Killing BT Thread");
try
bis.close();
bos.close();
bsocket.close();
bsocket = null;
catch (IOException e)
Log.e(BTAG, "IOException");
e.printStackTrace();
catch (Exception e)
Log.e(BTAG, "Exception");
e.printStackTrace();
try
Thread moribund = bThread;
bThread = null;
moribund.interrupt();
catch (Exception e)
Log.i(BTAG, "BT Thread Killed");
我发现使用正常的“bsocket = device.createRfcommSocketToServiceRecord(MY_UUID);”方法通常会导致我出现“服务发现失败”消息,所以我也尝试了“bsocket = (BluetoothSocket) m.invoke(device, Integer.valueOf(1));”方法。这更常见,但在我尝试连接时喜欢超时。
我在这里做错了什么?
【问题讨论】:
【参考方案1】:尝试监听传入的数据并在单独的线程中写入设备。这样您就可以分离阻塞调用。 你看过蓝牙聊天示例吗?该示例使用了类似的线程技术。
【讨论】:
解决了将数据写回套接字的问题。在尝试连接时,有时我仍然会收到“服务发现失败”和“设备或资源繁忙”消息。对此有什么想法吗?似乎它会连续连接好几次,然后它会抛出这些错误消息之一一段时间。 我发现蓝牙 Android 代码在连接到不同的设备时会始终如一地工作。我一直在测试的那个只在某些时候有效。奇怪的是,我在市场上发现的一款蓝牙 GPS 应用程序与该设备配合得很好,所以肯定有一种我还没有见过的不同连接方式。 当它不起作用时尝试浏览日志。可能是您遗漏了某些东西,或者该设备的实现方式与其他设备不同。 @Lance Lefebure - 你有没有弄清楚你上面描述的 BT 连接问题?我在运行 OS 的 Android 设备上遇到了同样的问题 @c12 - 我认为这很好,因为我没有对此有任何抱怨。写这篇文章已经有一段时间了,我不记得我做了什么,但我的应用程序是开源的,所以请随意看看:lefebure.com/software/android-ntripclient【参考方案2】:如果您的目标是 2.3 及更高版本(目前已安装 on over 50% 的 Android 设备),您可以使用 createInsecureRfcommSocketToServiceRecord 方法与设备进行通信,这肯定会使其更好、更易于连接。
【讨论】:
嘿,你能帮帮我吗..??***.com/questions/23648942/…以上是关于如何让蓝牙 RFCOMM 始终如一地工作?的主要内容,如果未能解决你的问题,请参考以下文章
蓝牙的rfcomm和spp都是串口的协议,他们之间有啥区别呢?