Android:在 SPP 蓝牙设备之间切换 [英] Android: Switch between SPP Bluetooth Devices

查看:33
本文介绍了Android:在 SPP 蓝牙设备之间切换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两台不同的蓝牙打印机.Bixolon SPP-R200 和富士通 FTP-628WSL110.我可以分别连接到它们中的每一个(使用三星 Galaxy SII)打印,断开连接并重新连接就好了.但是,如果我关闭 Bixolon 并尝试与富士通配对(以前未配对,Bixolon 仍配对),则在尝试连接到创建的套接字时会失败.反之亦然.

错误信息如下:

07-02 13:00:11.040: E/MyApp.BluetoothConnection(9380): 无法连接到 rfcomm 套接字.07-02 13:00:11.040: E/MyApp.BluetoothConnection(9380): java.io.IOException: 服务发现失败07-02 13:00:11.040: E/MyApp.BluetoothConnection(9380): 在 android.bluetooth.BluetoothSocket$SdpHelper.doSdp(BluetoothSocket.java:406)07-02 13:00:11.040: E/MyApp.BluetoothConnection(9380): 在 android.bluetooth.BluetoothSocket.connect(BluetoothSocket.java:217)07-02 13:00:11.040: E/MyApp.BluetoothConnection(9380): 在 MyApp.BluetoothConnection.connect(BluetoothConnection.java:171)07-02 13:00:11.040: E/MyApp.BluetoothConnection(9380): 在 MyApp.AbstractBluetoothPrinter.connect(AbstractBluetoothPrinter.java:34)

这是进行连接尝试的代码,在解释的情况下失败的行是 btSocket.connect();- 例外见上:

/** 在connect()中设置*/private BluetoothSocket btSocket = null;/** 在 connect() 之前设置 */私有BluetoothSocket btDevice;公共布尔连接(){尝试 {btSocket = btDevice.createRfcommSocketToServiceRecord("00001101-0000-1000-8000-00805F9B34FB");if (btDevice.getName().startsWith("FTP")) {//富士通打印机的特殊处理SystemClock.sleep(1000);}} catch (Throwable e) {LogCat.e(TAG, "无法创建 rfcomm 套接字.", e);返回假;}尝试 {//如果蓝牙发现正在进行,则停止蓝牙发现BluetoothHandler.cancelDiscovery();//这在描述的情况下失败btSocket.connect();} catch (Throwable e) {LogCat.e(TAG, "无法连接到 rfcomm 套接字.", e);返回假;}//获取流等...}

我使用相同 UUID连接到两台设备(但一次只打开一个设备,它们永远不会同时打开),SDK API 中众所周知的 SPP UUID:

00001101-0000-1000-8000-00805F9B34FB

这让我想知道:难道我需要为每个设备使用不同的 UUID?如果是,有什么想法吗?

解决方案

好的,在尝试了几天不同的解决方案后,我现在可以在上述打印机之间切换了.由于我不完全确定我的哪些措施是成功的原因,我将列出所有措施,因此偶然发现这篇文章的人将有一些关于如何解决他的蓝牙问题的线索.但是我非常确定一件事:您不需要不同的 UUID 来连接两台不同的打印机 - 您可以使用相同的 UUID(但我只打开其中一个).

我缓存了上次打印的设备 - 但与之前不同的是我不再缓存实际的 BluetoothDevice,而我只缓存它的mac地址,可通过以下方式获得:

BluetoothDevice bluetoothDevice;//通过查看配对设备或开始发现来获取BluetoothDevicebluetoothDevice.getAddress();

getAddress() 返回一个字符串:设备的硬件地址.我缓存该 mac 地址,下次用户想要打印时,我将缓存的 mac 地址与所有配对打印机的 mac 地址进行匹配 - 如果 mac 地址匹配其中之一,我尝试连接到该打印机.如果失败,我会重置缓存的 mac 地址并尝试通过首先检查我的配对设备是否可以连接来找到另一台设备(如果我可以成功连接,我会相应地更新缓存的 mac 地址),如果失败,我会启动一个寻找其他潜在设备的蓝牙发现.

现在为了不让我的一台打印机打开任何套接字连接,我的例程如下(我将省略我在每次调用中包装的 try-catch 以简化读取):

创建套接字

BluetoothSocket btSocket = btDevice.createRfcommSocketToServiceRecord(MY_UUID);

MY_UUID 是指用于连接到 SPP 设备的众所周知的 UUID:

00001101-0000-1000-8000-00805F9B34FB

如果套接字创建 失败(这种情况很少见,如果发生这种情况很可能是由于权限不足或蓝牙被禁用/不可用),我们无法继续进行,因为我们需要一个套接字来连接.因此,在您的 catch 块中,您应该触发 disconnect 方法(稍后会详细介绍).

连接到创建的套接字

bSocket.connect();

如果连接失败,我们将无法继续进行,因为我们需要一个有效的套接字连接来获取输入和输出流.因此,在您的 catch 块中,您应该触发 disconnect 方法(稍后会详细介绍).

获取输入输出流

下一步是从套接字获取输入和输出流.我在运行几次的 for 循环中执行此操作(5 次应该足够了) - 在每次迭代中,我检查是否有输出流,如果没有,我尝试获取它,对于输入流也是如此.在循环结束时,我检查是否有我的两个流,如果有,我退出循环(和整个连接方法),如果没有,我继续循环并重试.通常我会在第一次循环迭代中获得两个流,但有时我需要两到三次迭代才能获得两个流.

如果我到达循环声明之后的代码,我显然没有得到我的流或其他错误.此时认为连接失败,我执行断开连接代码(清理打开的流和套接字,稍后会详细介绍).

读/写

现在您已连接到目标蓝牙设备,您可以执行读写操作.完成后,您应该通过关闭所有流和套接字来清理,下一段将详细介绍:断开连接.请记住:如果在读/写操作过程中发生异常,请务必触发断开连接方法以清理您的资源.如果您的打印机需要某种初始化命令,请确保在连接到打印机后和执行读/写操作之前立即发送该命令.

断开连接

通常有两种情况您应该断开连接:

  • 完成读/写操作后
  • 如果过程中发生异常,清理你的资源

关闭您的信息流

你要做的第一件事是清理你的流,检查你的输入和输出流,如果它们不为空,关闭它们并将它们设置为空.确保将每个操作(关闭输入流、关闭输出流等)包装到自己的 try-catch 中,否则无法执行一次清理(因为引发异常)将跳过所有其他清理措施.

关闭插座

既然您已经确保您的输入流已被清理,请继续关闭您的套接字连接并将其设置为 null.

还有一件事:我的断开连接方法的开头和结尾都有一个 Thread.sleep.一开始的时间大约为 2.5 秒(= 2500 毫秒),目的是确保打印机没有其他任何事情(例如待处理的读/写操作或打印机仍在打印等).第二个 Thread.sleep 在我的断开连接方法的末尾,大约有 800 毫秒长.最后睡眠的原因与我在关闭套接字后立即尝试打开一个新套接字时遇到的问题有关.有关更多详细信息,请参阅此答案.

问题?

如果有人对我的 OP 或我的回答有任何疑问,请在评论中告诉我,我会尽力回答.

I have two different Bluetooth Printers. Bixolon SPP-R200 and Fujitsu FTP-628WSL110. I can connect to each of them separately (using a Samsung Galaxy SII) print, disconnect and reconnect just fine. However, if I switch off the Bixolon and try to pair with the Fujitsu (previously unpaired, Bixolon is still paired), then it fails when trying to connect to the created socket. Same the other way around.

Here is the error message:

07-02 13:00:11.040: E/MyApp.BluetoothConnection(9380): Failed to connect to rfcomm socket.
07-02 13:00:11.040: E/MyApp.BluetoothConnection(9380): java.io.IOException: Service discovery failed
07-02 13:00:11.040: E/MyApp.BluetoothConnection(9380):  at android.bluetooth.BluetoothSocket$SdpHelper.doSdp(BluetoothSocket.java:406)
07-02 13:00:11.040: E/MyApp.BluetoothConnection(9380):  at android.bluetooth.BluetoothSocket.connect(BluetoothSocket.java:217)
07-02 13:00:11.040: E/MyApp.BluetoothConnection(9380):  at MyApp.BluetoothConnection.connect(BluetoothConnection.java:171)
07-02 13:00:11.040: E/MyApp.BluetoothConnection(9380):  at MyApp.AbstractBluetoothPrinter.connect(AbstractBluetoothPrinter.java:34)

Here is the code, which makes the connection attempt, the line that fails under the explained circumstances is btSocket.connect(); - exception see above:

/** Is set in connect() */
private BluetoothSocket btSocket = null;
/** Is set prior to connect() */
private BluetoothSocket btDevice;

public boolean connect(){

        try {
            btSocket = btDevice.createRfcommSocketToServiceRecord("00001101-0000-1000-8000-00805F9B34FB");
            if (btDevice.getName().startsWith("FTP")) {
                //Special treatment for the fujitsu printer
                SystemClock.sleep(1000);
            }
        } catch (Throwable e) {
            LogCat.e(TAG, "Failed to create rfcomm socket.", e);
            return false;
        }

        try {
            // Stop Bluetooth discovery if it's going on
            BluetoothHandler.cancelDiscovery();
            // This fails under the described circumstances
            btSocket.connect();
        } catch (Throwable e) {
            LogCat.e(TAG, "Failed to connect to rfcomm socket.", e);
            return false;
        }

        // Obtain streams etc...
}

I am using the same UUID to connect to both devices (but only one device is switched on at a time, they're never switched on at the same time), the well known SPP UUID from the SDK API:

00001101-0000-1000-8000-00805F9B34FB

Which makes me wonder: Could it be, that I need a different UUID for each device? If yes any idea which?

解决方案

Ok after several days of trying different solutions, I am now able to switch between the afore mentioned printers. Since I am not entirely sure which of my measures was the reason for succeeding, I'll list them all, so someone stumbling upon this post will have some clues on how to fix his bluetooth issues. One thing however I am quite sure about: You don't need different UUIDs to connect two different printers - you can use the same UUID (But I only ever have one of them switched on).

I cache the device that was last printed to - however unlike before I no longer cache the actual BluetoothDevice, instead I only cache it's mac address which is obtainable through:

BluetoothDevice bluetoothDevice; 

//Obtain BluetoothDevice by looking through paired devices or starting discovery

bluetoothDevice.getAddress(); 

getAddress() returns a String: The hardware address of the device. I cache that mac address and next time the user wants to print, I match the cached mac address against the mac addresses of all paired printers - if the mac address matches one of these, I try to connect to that printer. If that fails, I reset my cached mac address and try to find another device by first checking my paired devices if one of them can connect (if I can successfully connect I update my cached mac address accordingly), and if that fails I start a bluetooth discovery looking for other potential devices.

Now in order to not leave any socket connections open to one of my printers my routine is as follows (I'll leave out the try-catches I have wrapped around each call to ease the read):

Create the socket

BluetoothSocket btSocket = btDevice.createRfcommSocketToServiceRecord(MY_UUID);

The MY_UUID refers to the well known UUID used for connecting to SPP devices:

00001101-0000-1000-8000-00805F9B34FB

If the socket creation fails (which is rare and if it happens it's most likely due to insufficient permissions or bluetooth being disabled/not available), we can't proceed further, as we need a socket to connect to. Hence, in your catch block you should trigger the disconnect method (more on that later).

Connect to the created socket

bSocket.connect();

If the connect fails, we can't proceed further, as we need a valid socket connection to obtain the input and output streams. Hence, in your catch block you should trigger the disconnect method (more on that later).

Obtain the input and output stream

The next step would be to obtain the input and output streams from the socket. I do this in a for loop which runs a couple of times (5 times should be enough) - in each iteration I check if I have the output stream, if not, I try to obtain it, same for the input stream. At the end of the loop I check if I have both my streams, if yes I exit the loop (AND the whole connect method), if no, I proceed with the loop and try again. Usually I get both my streams in the first loop-iteration, however sometimes I need two or three iterations to obtain both streams.

If I reach the code that follows after the loop declaration I obviously did not get my streams or something else went wrong. At this point the connect is considered to have failed and I execute my disconnect code (which cleans up open streams & sockets, more on that later).

Read/Write

Now that you have a connection to your target bluetooth device you can perform read and write operations. Once you're done you should cleanup by closing all streams and sockets, more on this in the next paragraph: Disconnecting. Remember: If an exception occurs during read/write operations, be sure to trigger the disconnect method in order to cleanup your resources. If your printer needs some kind of initialization command, be sure to send that right after connecting to the printer and before performing your read/write operations.

Disconnecting

There are usually two occasions on which you should disconnect:

  • Once you're done with your read/write operations
  • If an exception occurred somewhere along the way, to cleanup your resources

Close your streams

The first thing you want to do is cleanup your streams, check both, your input and output stream, if they're not null, close them and set them to null. Be sure to wrap each operation (closing the input stream, closing the output stream etc...) into its own try-catch as otherwise failing to do one cleanup (Because an exception is raised) will skip all other cleanup measures.

Close the socket

Now that you've made sure your input streams are cleaned up, proceed to closing your socket connection and setting it to null thereafter.

One more thing: I have a Thread.sleep at the beginning and end of my disconnect method. The one in the beginning is about 2.5 seconds (= 2500 milliseconds) long, the purpose is to make sure nothing else is going on with the printer (such as pending read/write operations or the printer still printing etc..). The second Thread.sleep is at the end of my disconnect method and is about 800 milliseconds long. The reason for that sleep at the end is related to the problems I had when trying to immediately open a new socket right after closing one. For more details please refer to this answer.

Questions?

In case anyone has questions related to my OP or my answer, please let me know in the comments and I'll try my best to answer them.

这篇关于Android:在 SPP 蓝牙设备之间切换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆