489次成功连接后android蓝牙连接失败
Posted
技术标签:
【中文标题】489次成功连接后android蓝牙连接失败【英文标题】:android bluetooth connection fails after 489 successful connections 【发布时间】:2014-03-28 03:16:53 【问题描述】:不幸的是,我在使用 android 的蓝牙时遇到了一些问题。对于我的测试环境,我使用带有 Android 4.4.2 的 Nexus 4。
我的 PC 上有一个 Java 应用程序,它使用 bluecove 作为客户端建立 SPP 连接。该程序正在寻找一个特殊的服务名称并与我的安卓手机连接。之后它会向我的安卓手机发送 72 个字节并等待回复。当得到那个答案时,程序会休眠 3 秒,然后重新启动。
在我的安卓手机上,我有一个带有后台蓝牙监听器的应用程序,它在启动时启动。此应用程序基于 BluetoothChat 示例演示。接收蓝牙数据时,我会检查传入数据并发送回答案。
一切正常。但是在 489 个蓝牙连接之后,android 应用程序失败,并在 PC-java-app 运行时出现以下错误 sn-p:
getBluetoothService() called with no BluetoothManagerCallback
Shutting down VM
threadid=1: thread exiting with uncaught exception (group=0x41b34ba8)
FATAL EXCEPTION: main
Process: de.tum.lme.diamantum:remote_blue, PID: 21567
java.lang.NullPointerException: FileDescriptor must not be null
at android.os.ParcelFileDescriptor.<init>(ParcelFileDescriptor.java:174)
at android.os.ParcelFileDescriptor$1.createFromParcel(ParcelFileDescriptor.java:905)
at android.os.ParcelFileDescriptor$1.createFromParcel(ParcelFileDescriptor.java:897)
at android.bluetooth.IBluetooth$Stub$Proxy.createSocketChannel(IBluetooth.java:1355)
at android.bluetooth.BluetoothSocket.bindListen(BluetoothSocket.java:349)
at android.bluetooth.BluetoothAdapter.createNewRfcommSocketAndRecord(BluetoothAdapter.java:1055)
at android.bluetooth.BluetoothAdapter.listenUsingRfcommWithServiceRecord(BluetoothAdapter.java:976)
at com.test.btconn.BluetoothHandling$AcceptThread.<init>(BluetoothHandling.java:449)
at com.test.btconn.BluetoothHandling.start(BluetoothHandling.java:216)
at com.test.btconn.BluetoothListenerService.setupBtSockets(BluetoothListenerService.java:330)
at com.test.btconn.BluetoothListenerService.manageBtState(BluetoothListenerService.java:249)
at com.test.btconn.BluetoothListenerService.setBtStateDisconnected(BluetoothListenerService.java:383)
at com.test.btconn.BluetoothListenerService.access$5(BluetoothListenerService.java:378)
at com.test.btconn.BluetoothListenerService$2.handleMessage(BluetoothListenerService.java:421)
所以应用程序的 ParcelFileDescriptor 有问题,它突然为空。但是为什么呢?
上述所有情况也会在 PC-java-app 上更改暂停时间时发生,使用各种数据大小来传输和使用不同的智能手机。当使用反射“listenUsingRfcommWithServiceRecord”时,同样会在 505 次传输之后发生。也使用唤醒锁没有任何改变。
顺便说一句,我在使用 BluetoothChat 示例时得到了相同的行为。
那么,有没有人暗示,会发生什么?
更新:
BluetoothServerSocket 在每次连接后关闭,如果蓝牙状态为 3,则关闭 BluetoothSocket。
【问题讨论】:
我想知道您的旧套接字是否没有关闭,因此您最终超出了某些系统或进程限制并且无法创建新的。 这与***.com/questions/21166222/…有关吗? 知道失败的原因吗? 想知道 4.4.3 是否解决了这个问题。有大量与 BT 相关的修复 您的设备似乎超过了允许的最大 fd 数。在这里查看***.com/questions/13262339/…。你确定你的应用关闭了所有资源吗? 【参考方案1】:如果您在应用程序中使用蓝牙作为服务器,我遇到了与此相同的问题,除了我的问题是当我进行反射时我的文件描述符 mPfd 为空。我能够使用这种方法关闭文件,希望这可以帮助。
我使用 ParceFileDescriptor 从 BluetoothSocket 中的 LoaclSocket 的 id/index 获取文件。 诠释 mfd = 0; 字段 socketField = null; LocalSocket mSocket = null;
try
socketField = btSocket.getClass().getDeclaredField("mSocket");
socketField.setAccessible(true);
mSocket = (LocalSocket)socketField.get(btSocket);
catch(Exception e)
Log ( "Exception getting mSocket in cleanCloseFix(): " + e.toString());
if(mSocket != null)
FileDescriptor fileDescriptor =
mSocket.getFileDescriptor();
String in = fileDescriptor.toString();
//regular expression to get filedescriptor index id
Pattern p = Pattern.compile("\\[(.*?)\\]");
Matcher m = p.matcher(in);
while(m.find())
Log ( "File Descriptor " + m.group(1));
mfd = Integer.parseInt(m.group(1));
break;
//Shutdown the socket properly
mSocket.shutdownInput();
mSocket.shutdownOutput();
mSocket.close();
mSocket = null;
try socketField.set(btSocket, mSocket);
catch(Exception e)
Log ("Exception setting mSocket = null in cleanCloseFix(): " + e.toString());
//Close the file descriptor when we have it from the Local Socket
try
ParcelFileDescriptor parcelFileDescriptor = ParcelFileDescriptor.adoptFd(mfd);
if (parcelFileDescriptor != null)
parcelFileDescriptor.close();
Log ( "File descriptor close succeed : FD = " + mfd);
catch (Exception ex)
Log ( "File descriptor close exception " + ex.getMessage());
【讨论】:
【参考方案2】:这是 Android 4.2 - 4.4.4(和 4.4w & L 预览版)中的 BluetoothSocket.close() 错误...在内部,它调用 mPfd.detachFd(),然后实际上并没有释放底层文件描述符。解决方法是改为调用 mPfd.close() 并设置 mPfd=null,这正是 Android 5.0 现在的做法。
您主要可以使用此处发布的其他解决方案来解决此问题,这些解决方案调用 mPfd.close(),然后调用您平台的 BluetoothSocket.close(),但我发现这对我来说还不够,并且可能会发生一些奇怪的事情。我更进一步,还先清理了 mSocket 并将其设置为 null,然后调用 mPfd.close() 并将其设置为 null,最后调用设置 mSocketState 所需的 BluetoothSocket.close()。
public static void cleanClose(BluetoothSocket btSocket)
if(btSocket == null)
return;
if(Build.VERSION.SDK_INT >= 17 && Build.VERSION.SDK_INT <= 20)
try cleanCloseFix(btSocket);
catch (Exception e)
Log.d(sLogName, "Exception during BluetoothSocket close bug fix: " + e.toString());
//Go on to call BluetoothSocket.close() too, because our code didn't do quite everything
//Call BluetoothSocket.close()
try btSocket.close();
catch (Exception e)
Log.d(sLogName, "Exception during BluetoothSocket close: " + e.toString());
private static void cleanCloseFix(BluetoothSocket btSocket) throws IOException
synchronized(btSocket)
Field socketField = null;
LocalSocket mSocket = null;
try
socketField = btSocket.getClass().getDeclaredField("mSocket");
socketField.setAccessible(true);
mSocket = (LocalSocket)socketField.get(btSocket);
catch(Exception e)
Log.d(sLogName, "Exception getting mSocket in cleanCloseFix(): " + e.toString());
if(mSocket != null)
mSocket.shutdownInput();
mSocket.shutdownOutput();
mSocket.close();
mSocket = null;
try socketField.set(btSocket, mSocket);
catch(Exception e)
Log.d(sLogName, "Exception setting mSocket = null in cleanCloseFix(): " + e.toString());
Field pfdField = null;
ParcelFileDescriptor mPfd = null;
try
pfdField = btSocket.getClass().getDeclaredField("mPfd");
pfdField.setAccessible(true);
mPfd = (ParcelFileDescriptor)pfdField.get(btSocket);
catch(Exception e)
Log.d(sLogName, "Exception getting mPfd in cleanCloseFix(): " + e.toString());
if(mPfd != null)
mPfd.close();
mPfd = null;
try pfdField.set(btSocket, mPfd);
catch(Exception e)
Log.d(sLogName, "Exception setting mPfd = null in cleanCloseFix(): " + e.toString());
//synchronized
【讨论】:
【参考方案3】:问题似乎与您设备上文件描述符的限制有关。有针对该问题的报告here
在蓝牙套接字创建期间一个新的 fd 是two new FDs 从系统中获取。似乎您没有正确关闭之前的 BT 连接,因此使用的 FD 数量稳步增加,直到您达到限制。
为避免这种情况,您至少必须在完成操作后从listenUsingRfcommWithServiceRecord()
调用收到的BluetoothServerSocket 上调用close()
。您还应该检查您是否保留了连接到 BT 连接的其他资源,并尽可能释放它们。
正如这里所要求的,如何强制关闭 BluetoothServerSocket 的 ParcelFileDescriptor。 小心:它可能会破坏一切!
您必须访问 BluetoothServerSocket 的 mSocket 字段才能访问底层 BluetoothSocket。这个 BluetoothSocket 在 mPfd 字段中保存 ParcelFileDescriptor。您可以致电close()
。由于这两个字段都不可见,您将不得不使用 Reflections:
public void closePFD(BluetoothServerSocket closeMe) throws AllKindOfExceptionsThatYouHaveToHandle
Field mSocketFld = closeMe.getClass().getDeclaredField("mSocket");
mSocketFld.setAccessible(true);
BluetoothSocket btsock = (BluetoothSocket)mSocketFld.get(closeMe);
Field mPfdFld = btsock.getClass().getDeclaredField("mPfd");
mPfdFld.setAccessible(true);
ParcelFileDescriptor pfd = (ParcelFileDescriptor)mPfdFld.get(btsock);
pfd.close();
这将关闭 BluetoothServerSocket。如果您只想从 BTServerSockets 接受方法中关闭 BluetoothSocket,您可以省略获取 mSocket 的部分,如 jitain sharmas answer 所示。
【讨论】:
谢谢。同时我意识到我们使用的是 3rd 方库,所以我会向他们报告一个错误。 我们可以重置 ParcelFileDescriptor 吗? @jitainsharma 这应该由系统在关闭和处理连接/流期间处理......如果您获得对 FD 的引用,您可以使用它的close()
方法,但这会创建如果它仍在其他地方使用,则会出现错误。
我已经解决了文件描述符超出限制,是通过使用反射的方法关闭文件描述符。看来Android蓝牙库里面有一些韭菜。
这可以解决问题。需要注意的是,只有在尝试 BluetoothSocket.close() 之前这样做才能解决泄漏问题。【参考方案4】:
我对我提出的问题的解决方法:
private synchronized void clearFileDescriptor()
try
Field field = BluetoothSocket.class.getDeclaredField("mPfd");
field.setAccessible(true);
ParcelFileDescriptor mPfd = (ParcelFileDescriptor)field.get(socket);
if(null == mPfd)
return;
mPfd.close();
catch(Exception e)
Log.w(SensorTicker.TAG, "ParcelFileDescriptor could not be cleanly closed.");
上面是我编写的关闭文件描述符的方法,下面是我使用此代码时的方法: 无论何时,套接字都必须关闭:
private synchronized void cleanClose()
if (socket != null)
try
clearFileDescriptor();
//clearLocalSocket();
socket.close();
catch (IOException e)
Log.w(SensorTicker.TAG, "Socket could not be cleanly closed.");
我也尝试过使用我编写的 clearLocalSocket() 方法,但没有用于我的问题。所以我试图关闭 FileDescriptor。 希望它能帮助您和其他面临同样问题的人。
【讨论】:
解决问题!应该注意的是,在尝试 socket.close() 之前,您需要始终执行 mPfd 操作(即 clearFileDescriptor() )。如果首先调用 socket.close(),PFD 泄漏将继续存在。您可能还想在 mPfd 内容之前进行 LocalSocket (mSocket) 清理。我看到一些奇怪的事情发生了,直到我在 BluetoothSocket obj 上添加并同步()它。基本复制Android 5.0代码:grepcode.com/file/repository.grepcode.com/java/ext/…以上是关于489次成功连接后android蓝牙连接失败的主要内容,如果未能解决你的问题,请参考以下文章