安卓:SPP蓝牙设备之间切换蓝牙、设备、SPP

2023-09-05 11:03:29 作者:萌妹子也有彪悍时

我有两个不同的蓝牙打印机。 BIXOLON SPP-R200和富士通FTP-628WSL110。我可以连接到他们每个人分开(用的是三星的Galaxy SII)打印,断开并重新连接就好了。不过,如果我关掉BIXOLON并尝试配对的富士通(previously未成,BIXOLON仍配对),然后尝试连接到创建套接字时失败。同周围的其他方法。

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.

下面是错误消息:

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)

下面是code,这使得连接尝试,即在说明情况失败线路btSocket.connect(); - 上面的异常见:

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...
}

我使用的同一个 UUID连接到两个设备(但只有一个设备处于开机状态的时间,他们从来不开机的同时),则公知的SPP的UUID从SDK API:

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

这让我纳闷:莫非,我需要一个不同的UUID为每个设备?如果有任何想法有哪些?

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

推荐答案

确定后,尝试不同的解决方案好几天了,我现在能够在上述提到的打印机之间进行切换。因为我不能完全肯定这是我的措施是为成功的原因,我会全部列出来,所以有人根据这个帖子绊脚石将对如何解决自己的蓝牙问题的一些线索。有一件事,但是我敢肯定:你并不需要不同的UUID来连接两个不同的打印机 - 您可以使用相同的UUID(但我永远只能有其中之一接通)

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).

我的缓存上次打印的设备 - 但是不像以前我不再缓存实际的BluetoothDevice,相反,我只是缓存它的MAC地址,这是获得通过:

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()返回一个字符串:设备的硬件地址。我缓存的MAC地址和下一次用户想打印,我匹配所有匹配打印机的MAC地址缓存的MAC地址 - 如果MAC地址匹配这些中的一个,我尝试连接到该打印机。如果失败,重置我的缓存的MAC地址并试图找到其他设备首先检查我的配对设备如果其中一个可连接(如果我可以成功连接我相应地更新我的缓存的MAC地址),如果失败我开始蓝牙发现寻找其他潜在的设备。

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):

创建插座

BluetoothSocket btSocket = btDevice.createRfcommSocketToServiceRecord(MY_UUID);

的MY_UUID指用于连接到SPP设备的公知的UUID:

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

00001101-0000-1000-8000-00805F9B34FB

如果在socket创建失败(这是罕见的,如果碰巧它很可能是由于权限不足或蓝牙被禁用/不可用),我们无法继续进行,因为我们需要一个套接字连接。因此,在你的catch块,你应该触发断开连接方法(稍后更多)。

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).

连接到创建的套接字

bSocket.connect();

如果连接失败,我们无法继续进行,因为我们需要一个有效的套接字连接来获取输入和输出流。因此,在你的catch块,你应该触发断开连接方法(稍后更多)。

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).

获取输入和输出流

下一步骤将是获得从插座的输入和输出流。我这样做是在它运行了好几次(5次应该是足够了)一个for循环 - 在每次迭代我检查我是否有输出流,如果不是,我尝试获得它,相同的输入流。在循环结束时,我检查,如果我有我的两个流,如果是我退出循环(和整个连接方式),如果没有,我着手进行循环,然后重试。通常我得到的第一个循环迭代我的两个流,但我有时需要两个或三个迭代来获得两个流。

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.

如果我到了code后面的循环声明后,我显然没有得到我流还是其他什么东西出了问题。此时的连接被认为已经失败,我执行我的脱节code。(这样做可以清理开流和放大器;插座,稍后更多)

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).

读/写

现在,你有你的目标蓝牙设备的连接,你可以执行读取和写入操作。一旦你做了你应该清理关闭所有流,插座,更多关于这个在未来的一段:断开连接。请记住:如果在读/写操作发生异常时,一定要以触发断开的方法来清理你的资源。如果您的打印机需要某种形式的初始化命令,一定要连接到打印机和执行的读/写操作之前之后发出这一权利。

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.

断开

通常有两种场合上,您应该断开:

There are usually two occasions on which you should disconnect:

一旦你与​​你的读/写操作完成 如果某个地方,一路上发生了异常,清理你的资源

闭上你的流

您想要做的第一件事就是清理你的数据流,同时检查,你的输入和输出流,如果他们不为空,关闭它们,并将它们设置为null。请确保包裹每个操作(关闭输入流,关闭输出流等)到其自己的try-catch,否则不能做一个清理(由于抛出一个例外),将跳过所有其他清理措施。

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.

关闭插座

现在,你已经取得了一定的输入流被清理,着手关闭您的插座连接,并设置其随后为null。

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

一件事:我有一个视频下载我的断开方法的开始和结束。一个在一开始是2.5秒(= 2500毫秒)长,目的是确保没有其他打印机事情(如未决的读/写操作或打印机仍然打印等)。第二个视频下载是在我的断开方法的结束,约800毫秒长。其原因睡眠到底是关系到我试图立即关闭后立刻打开一个新的socket时的问题。欲了解更多详情,请参阅这个答案。

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.

问题?

在任何情况下,已经与我的OP或者我的答案的问题,请让我知道在评论,我会尽我所能回答这些问题。

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.