繁体   English   中英

489次成功连接后,android蓝牙连接失败

[英]android bluetooth connection fails after 489 successful connections

不幸的是,我的android蓝牙有一些问题。 对于我的测试环境,我使用的是带有Android 4.4.2的Nexus 4。

我的PC上有一个Java应用程序,它使用bluecove作为客户端进行SPP连接。 该程序正在寻找一个特殊的服务名称,并与我的Android手机连接。 然后它发送72个字节到我的Android手机,并等待答案。 得到答案时,程序会睡3秒钟而不是重新开始。

在我的Android手机上,我有一个带有后台蓝牙监听器的应用程序,它在启动时启动。 此应用程序基于BluetoothChat示例演示。 当接收蓝牙数据时,我检查传入的数据并发回答案。

一切正常。 但是在489蓝牙连接之后,Android应用程序在PC-java-app正在进行时失败并显示以下错误代码:

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样本时我也有同样的行为。

所以,有人提示,会发生什么?

更新:

如果蓝牙状态为3,则每次连接后BluetoothServerSocket都会关闭,BluetoothSocket会关闭。

问题似乎与您设备上的文件描述符限制有关。 没有为这个问题的报告在这里

在创建蓝牙套接字期间, 新的fd是从系统获取的两个新FD 您似乎没有正确关闭以前的BT连接,因此在您达到限制之前,已使用的FD数量会稳步增加。

为了避免这种情况,你会至少要调用close()在你从收到BluetoothServerSocket listenUsingRfcommWithServiceRecord()完成操作它后调用。 您还应该检查是否要保留连接到BT连接的其他资源,并在可能的情况下释放它们。


这里要求的是如何强制关闭BluetoothServerSocket的ParcelFileDescriptor。 小心:它可能会破坏事情!

您必须访问BluetoothServerSocketmSocket字段才能访问底层的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所示

我对我提出的问题的解决方法是:

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.");
        }
    }

所以上面是我编写的关闭filedescriptor的方法,以及下面我使用此代码的方法:每当socket必须关闭时:

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。 希望它能帮助你和其他人面对同样的问题。

这是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
}

如果您在应用程序中使用蓝牙作为服务器,我遇到了与此相同的问题,除了我的问题是当我进行反射时,我在文件描述符mPfd上变为null。 我能够使用这种方法关闭文件,希望这可以帮助。

我使用ParceFileDescriptor从BluetoothSocket中的LoaclSocket通过id / index获取文件。 int 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());
        }
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM