IOException: การอ่านล้มเหลวซ็อกเก็ตอาจปิด - บลูทู ธ บน Android 4.3


101

ขณะนี้ฉันกำลังพยายามจัดการกับข้อยกเว้นแปลก ๆ เมื่อเปิด BluetoothSocket บน Nexus 7 (2012) ด้วย Android 4.3 (สร้าง JWR66Y ฉันเดาว่าอัปเดต 4.3 ครั้งที่สอง) ฉันได้เห็นโพสต์ที่เกี่ยวข้องบางรายการ (เช่น/programming/13648373/bluetoothsocket-connect-throwing-exception-read-failed ) แต่ดูเหมือนจะไม่มีวิธีแก้ปัญหาสำหรับปัญหานี้ ตามที่แนะนำในเธรดเหล่านี้การจับคู่ใหม่ไม่ได้ช่วยอะไรและการพยายามเชื่อมต่อตลอดเวลา (ผ่านการวนซ้ำโง่ ๆ ) ก็ไม่มีผลเช่นกัน

ฉันกำลังจัดการกับอุปกรณ์ฝังตัว (อะแดปเตอร์ในรถยนต์ที่ไม่ใช่ชื่อ OBD-II คล้ายกับhttp://images04.olx.com/ui/15/53/76/1316534072_254254776_2-OBD-II-BLUTOOTH-ADAPTERSCLEAR-CHECK-ENGINE- ไฟพร้อมโทรศัพท์ของคุณ -Oceanside.jpg ) โทรศัพท์ Android 2.3.7 ของฉันไม่มีปัญหาในการเชื่อมต่อและ Xperia ของเพื่อนร่วมงาน (Android 4.1.2) ก็ใช้งานได้เช่นกัน Google Nexus อีกเครื่อง (ฉันไม่รู้ว่า 'One' หรือ 'S' แต่ไม่ใช่ '4') ก็ล้มเหลวด้วย Android 4.3

นี่คือตัวอย่างของการสร้างการเชื่อมต่อ กำลังทำงานในเธรดของตัวเองซึ่งสร้างขึ้นภายในบริการ

private class ConnectThread extends Thread {

    private static final UUID EMBEDDED_BOARD_SPP = UUID
        .fromString("00001101-0000-1000-8000-00805F9B34FB");

    private BluetoothAdapter adapter;
    private boolean secure;
    private BluetoothDevice device;
    private List<UUID> uuidCandidates;
    private int candidate;
    protected boolean started;

    public ConnectThread(BluetoothDevice device, boolean secure) {
        logger.info("initiliasing connection to device "+device.getName() +" / "+ device.getAddress());
        adapter = BluetoothAdapter.getDefaultAdapter();
        this.secure = secure;
        this.device = device;

        setName("BluetoothConnectThread");

        if (!startQueryingForUUIDs()) {
            this.uuidCandidates = Collections.singletonList(EMBEDDED_BOARD_SPP);
            this.start();
        } else{
            logger.info("Using UUID discovery mechanism.");
        }
        /*
         * it will start upon the broadcast receive otherwise
         */
    }

    private boolean startQueryingForUUIDs() {
        Class<?> cl = BluetoothDevice.class;

        Class<?>[] par = {};
        Method fetchUuidsWithSdpMethod;
        try {
            fetchUuidsWithSdpMethod = cl.getMethod("fetchUuidsWithSdp", par);
        } catch (NoSuchMethodException e) {
            logger.warn(e.getMessage());
            return false;
        }

        Object[] args = {};
        try {
            BroadcastReceiver receiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    BluetoothDevice deviceExtra = intent.getParcelableExtra("android.bluetooth.device.extra.DEVICE");
                    Parcelable[] uuidExtra = intent.getParcelableArrayExtra("android.bluetooth.device.extra.UUID");

                    uuidCandidates = new ArrayList<UUID>();
                    for (Parcelable uuid : uuidExtra) {
                        uuidCandidates.add(UUID.fromString(uuid.toString()));
                    }

                    synchronized (ConnectThread.this) {
                        if (!ConnectThread.this.started) {
                            ConnectThread.this.start();
                            ConnectThread.this.started = true;
                            unregisterReceiver(this);
                        }

                    }
                }

            };
            registerReceiver(receiver, new IntentFilter("android.bleutooth.device.action.UUID"));
            registerReceiver(receiver, new IntentFilter("android.bluetooth.device.action.UUID"));

            fetchUuidsWithSdpMethod.invoke(device, args);
        } catch (IllegalArgumentException e) {
            logger.warn(e.getMessage());
            return false;
        } catch (IllegalAccessException e) {
            logger.warn(e.getMessage());
            return false;
        } catch (InvocationTargetException e) {
            logger.warn(e.getMessage());
            return false;
        }           

        return true;
    }

    public void run() {
        boolean success = false;
        while (selectSocket()) {

            if (bluetoothSocket == null) {
                logger.warn("Socket is null! Cancelling!");
                deviceDisconnected();
                openTroubleshootingActivity(TroubleshootingActivity.BLUETOOTH_EXCEPTION);
            }

            // Always cancel discovery because it will slow down a connection
            adapter.cancelDiscovery();

            // Make a connection to the BluetoothSocket
            try {
                // This is a blocking call and will only return on a
                // successful connection or an exception
                bluetoothSocket.connect();
                success = true;
                break;

            } catch (IOException e) {
                // Close the socket
                try {
                    shutdownSocket();
                } catch (IOException e2) {
                    logger.warn(e2.getMessage(), e2);
                }
            }
        }

        if (success) {
            deviceConnected();
        } else {
            deviceDisconnected();
            openTroubleshootingActivity(TroubleshootingActivity.BLUETOOTH_EXCEPTION);
        }
    }

    private boolean selectSocket() {
        if (candidate >= uuidCandidates.size()) {
            return false;
        }

        BluetoothSocket tmp;
        UUID uuid = uuidCandidates.get(candidate++);
        logger.info("Attempting to connect to SDP "+ uuid);
        try {
            if (secure) {
                tmp = device.createRfcommSocketToServiceRecord(
                        uuid);
            } else {
                tmp = device.createInsecureRfcommSocketToServiceRecord(
                        uuid);
            }
            bluetoothSocket = tmp;
            return true;
        } catch (IOException e) {
            logger.warn(e.getMessage() ,e);
        }

        return false;
    }

}

bluetoothSocket.connect()รหัสเป็นความล้มเหลวที่ ฉันได้รับjava.io.IOException: read failed, socket might closed, read ret: -1. นี่คือแหล่งที่มาที่สอดคล้องกันใน GitHub: https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L504 ซึ่งเรียกว่าผ่าน readInt () เรียกจากhttps : //github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L319

การถ่ายโอนข้อมูลเมตาบางส่วนของซ็อกเก็ตที่ใช้แล้วส่งผลให้เกิดข้อมูลต่อไปนี้ สิ่งเหล่านี้เหมือนกันทุกประการใน Nexus 7 และโทรศัพท์ 2.3.7 ของฉัน

Bluetooth Device 'OBDII'
Address: 11:22:33:DD:EE:FF
Bond state: 12 (bonded)
Type: 1
Class major version: 7936
Class minor version: 7936
Class Contents: 0
Contents: 0

ฉันมีอะแดปเตอร์ OBD-II อื่น ๆ (ขยายได้มากขึ้น) และใช้งานได้ทั้งหมด มีโอกาสไหมที่ฉันทำบางอย่างหายไปหรืออาจเป็นข้อบกพร่องใน Android


ฉันได้ลองวิธีแก้ปัญหาข้างต้นแล้ว sombody ช่วยในเรื่องนี้ได้
ไหม

คำตอบ:


131

ในที่สุดฉันก็พบวิธีแก้ปัญหาแล้ว ความมหัศจรรย์ซ่อนอยู่ภายใต้ประทุนของBluetoothDeviceคลาส (ดูhttps://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothDevice.java#L1037 )

ตอนนี้เมื่อฉันได้รับข้อยกเว้นนั้นฉันจะสร้างอินสแตนซ์ทางเลือกBluetoothSocketคล้ายกับซอร์สโค้ดด้านล่าง อย่างที่คุณเห็นการเรียกใช้วิธีการที่ซ่อนอยู่createRfcommSocketผ่านการสะท้อนกลับ ฉันไม่รู้ว่าทำไมวิธีนี้จึงถูกซ่อนไว้ ซอร์สโค้ดให้คำจำกัดความpublicว่า ...

Class<?> clazz = tmp.getRemoteDevice().getClass();
Class<?>[] paramTypes = new Class<?>[] {Integer.TYPE};

Method m = clazz.getMethod("createRfcommSocket", paramTypes);
Object[] params = new Object[] {Integer.valueOf(1)};

fallbackSocket = (BluetoothSocket) m.invoke(tmp.getRemoteDevice(), params);
fallbackSocket.connect();

connect()แล้วจะไม่ล้มเหลวอีกต่อไป ฉันยังคงประสบปัญหาบางประการ โดยทั่วไปบางครั้งสิ่งนี้จะบล็อกและล้มเหลว การรีบูตอุปกรณ์ SPP (ปิด / เสียบปลั๊ก) ช่วยในกรณีดังกล่าว บางครั้งฉันยังได้รับคำขอจับคู่อีกครั้งconnect()แม้ว่าอุปกรณ์จะเชื่อมต่ออยู่แล้วก็ตาม

อัพเดท:

นี่คือคลาสที่สมบูรณ์ซึ่งมีคลาสที่ซ้อนกันอยู่ สำหรับการนำไปใช้จริงสิ่งเหล่านี้อาจจัดเป็นชั้นเรียนแยกกัน

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.List;
import java.util.UUID;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.util.Log;

public class BluetoothConnector {

    private BluetoothSocketWrapper bluetoothSocket;
    private BluetoothDevice device;
    private boolean secure;
    private BluetoothAdapter adapter;
    private List<UUID> uuidCandidates;
    private int candidate;


    /**
     * @param device the device
     * @param secure if connection should be done via a secure socket
     * @param adapter the Android BT adapter
     * @param uuidCandidates a list of UUIDs. if null or empty, the Serial PP id is used
     */
    public BluetoothConnector(BluetoothDevice device, boolean secure, BluetoothAdapter adapter,
            List<UUID> uuidCandidates) {
        this.device = device;
        this.secure = secure;
        this.adapter = adapter;
        this.uuidCandidates = uuidCandidates;

        if (this.uuidCandidates == null || this.uuidCandidates.isEmpty()) {
            this.uuidCandidates = new ArrayList<UUID>();
            this.uuidCandidates.add(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
        }
    }

    public BluetoothSocketWrapper connect() throws IOException {
        boolean success = false;
        while (selectSocket()) {
            adapter.cancelDiscovery();

            try {
                bluetoothSocket.connect();
                success = true;
                break;
            } catch (IOException e) {
                //try the fallback
                try {
                    bluetoothSocket = new FallbackBluetoothSocket(bluetoothSocket.getUnderlyingSocket());
                    Thread.sleep(500);                  
                    bluetoothSocket.connect();
                    success = true;
                    break;  
                } catch (FallbackException e1) {
                    Log.w("BT", "Could not initialize FallbackBluetoothSocket classes.", e);
                } catch (InterruptedException e1) {
                    Log.w("BT", e1.getMessage(), e1);
                } catch (IOException e1) {
                    Log.w("BT", "Fallback failed. Cancelling.", e1);
                }
            }
        }

        if (!success) {
            throw new IOException("Could not connect to device: "+ device.getAddress());
        }

        return bluetoothSocket;
    }

    private boolean selectSocket() throws IOException {
        if (candidate >= uuidCandidates.size()) {
            return false;
        }

        BluetoothSocket tmp;
        UUID uuid = uuidCandidates.get(candidate++);

        Log.i("BT", "Attempting to connect to Protocol: "+ uuid);
        if (secure) {
            tmp = device.createRfcommSocketToServiceRecord(uuid);
        } else {
            tmp = device.createInsecureRfcommSocketToServiceRecord(uuid);
        }
        bluetoothSocket = new NativeBluetoothSocket(tmp);

        return true;
    }

    public static interface BluetoothSocketWrapper {

        InputStream getInputStream() throws IOException;

        OutputStream getOutputStream() throws IOException;

        String getRemoteDeviceName();

        void connect() throws IOException;

        String getRemoteDeviceAddress();

        void close() throws IOException;

        BluetoothSocket getUnderlyingSocket();

    }


    public static class NativeBluetoothSocket implements BluetoothSocketWrapper {

        private BluetoothSocket socket;

        public NativeBluetoothSocket(BluetoothSocket tmp) {
            this.socket = tmp;
        }

        @Override
        public InputStream getInputStream() throws IOException {
            return socket.getInputStream();
        }

        @Override
        public OutputStream getOutputStream() throws IOException {
            return socket.getOutputStream();
        }

        @Override
        public String getRemoteDeviceName() {
            return socket.getRemoteDevice().getName();
        }

        @Override
        public void connect() throws IOException {
            socket.connect();
        }

        @Override
        public String getRemoteDeviceAddress() {
            return socket.getRemoteDevice().getAddress();
        }

        @Override
        public void close() throws IOException {
            socket.close();
        }

        @Override
        public BluetoothSocket getUnderlyingSocket() {
            return socket;
        }

    }

    public class FallbackBluetoothSocket extends NativeBluetoothSocket {

        private BluetoothSocket fallbackSocket;

        public FallbackBluetoothSocket(BluetoothSocket tmp) throws FallbackException {
            super(tmp);
            try
            {
              Class<?> clazz = tmp.getRemoteDevice().getClass();
              Class<?>[] paramTypes = new Class<?>[] {Integer.TYPE};
              Method m = clazz.getMethod("createRfcommSocket", paramTypes);
              Object[] params = new Object[] {Integer.valueOf(1)};
              fallbackSocket = (BluetoothSocket) m.invoke(tmp.getRemoteDevice(), params);
            }
            catch (Exception e)
            {
                throw new FallbackException(e);
            }
        }

        @Override
        public InputStream getInputStream() throws IOException {
            return fallbackSocket.getInputStream();
        }

        @Override
        public OutputStream getOutputStream() throws IOException {
            return fallbackSocket.getOutputStream();
        }


        @Override
        public void connect() throws IOException {
            fallbackSocket.connect();
        }


        @Override
        public void close() throws IOException {
            fallbackSocket.close();
        }

    }

    public static class FallbackException extends Exception {

        /**
         * 
         */
        private static final long serialVersionUID = 1L;

        public FallbackException(Exception e) {
            super(e);
        }

    }
}

3
ว้าว! ทางออกที่ดี!
บ็อบ

2
@MD ไม่รู้เป็นไง ฉันเพิ่งทดสอบและพบว่ามันใช้งานได้
บ็อบ

1
มันใช้ไม่ได้กับ nexus 4 คุณช่วยบอกวิธีแก้ปัญหานี้ได้ไหม ฉันได้ลองเกือบทุกอย่างแล้ว ขอบคุณ.
Shah

3
@matthes ขอโทษที่ต้องพูด แต่แม้แต่วิธีแก้ปัญหาของคุณในการใช้ทางเลือกก็ยังไม่สามารถแก้ปัญหาของฉันได้ รับข้อผิดพลาดด้านล่าง Fallback failed. Cancelling. java.io.IOException: Connection refused กรุณาช่วย.
Tushar Banne

2
@matthes "SPP-Device (ปิด / เสียบปลั๊ก) ช่วยในกรณีเช่นนี้". คำสั่งเปิด / ปิดมีการประเมินต่ำที่สุดในโลก ฉันเสียเวลาไปแค่ 3 ชั่วโมงและทั้งหมดที่ฉันต้องทำคือเปิดและปิด -_-
Adz

99

ฉันมีปัญหาเดียวกันกับรหัสของฉันและเป็นเพราะตั้งแต่ android 4.2 บลูทู ธ สแต็คมีการเปลี่ยนแปลง ดังนั้นรหัสของฉันจึงทำงานได้ดีบนอุปกรณ์ที่ใช้ Android <4.2 ในอุปกรณ์อื่น ๆ ฉันได้รับข้อยกเว้นที่มีชื่อเสียง"อ่านล้มเหลวซ็อกเก็ตอาจปิดหรือหมดเวลาอ่าน ret: -1"

ปัญหาอยู่ที่socket.mPortพารามิเตอร์ เมื่อคุณสร้างซ็อกเก็ตของคุณโดยใช้socket = device.createRfcommSocketToServiceRecord(SERIAL_UUID);ที่mPortได้รับจำนวนเต็มค่า " -1 " และค่านี้ดูเหมือนว่าไม่ทำงานสำหรับหุ่นยนต์> = 4.2 ดังนั้นคุณจำเป็นต้องตั้งค่าเป็น " 1 " ข่าวร้ายคือcreateRfcommSocketToServiceRecordยอมรับเฉพาะ UUID เป็นพารามิเตอร์เท่านั้นและไม่mPortจำเป็นต้องใช้ aproach อื่น คำตอบโพสต์โดย@matthesยังทำงานสำหรับฉัน socket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1);แต่ฉันง่ายมัน เราจำเป็นต้องใช้ทั้งซ็อกเก็ต Attribs อันที่สองเป็นทางเลือก

ดังนั้นรหัสคือ (สำหรับเชื่อมต่อกับ SPP บนอุปกรณ์ ELM327):

BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();

    if (btAdapter.isEnabled()) {
        SharedPreferences prefs_btdev = getSharedPreferences("btdev", 0);
        String btdevaddr=prefs_btdev.getString("btdevaddr","?");

        if (btdevaddr != "?")
        {
            BluetoothDevice device = btAdapter.getRemoteDevice(btdevaddr);

            UUID SERIAL_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); // bluetooth serial port service
            //UUID SERIAL_UUID = device.getUuids()[0].getUuid(); //if you don't know the UUID of the bluetooth device service, you can get it like this from android cache

            BluetoothSocket socket = null;

            try {
                socket = device.createRfcommSocketToServiceRecord(SERIAL_UUID);
            } catch (Exception e) {Log.e("","Error creating socket");}

            try {
                socket.connect();
                Log.e("","Connected");
            } catch (IOException e) {
                Log.e("",e.getMessage());
                try {
                    Log.e("","trying fallback...");

                    socket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1);
                    socket.connect();

                    Log.e("","Connected");
                }
             catch (Exception e2) {
                 Log.e("", "Couldn't establish Bluetooth connection!");
              }
            }
        }
        else
        {
            Log.e("","BT device not selected");
        }
    }

58
สำหรับผู้ดูแล: เนื่องจากคุณลบคำตอบก่อนหน้านี้โดยไม่มีเหตุผลฉันจึงโพสต์อีกครั้ง
George Dima

2
ขอบคุณ George สำหรับข้อมูลเชิงลึกเกี่ยวกับmPortพารามิเตอร์! เวิร์กโฟลว์ยังคงเหมือนเดิมฉันเพิ่งห่อบางอย่างกับบางคลาสที่ใช้อินเทอร์เฟซ
แมตต์

5
ใช่ฉันบอกแล้วว่าโซลูชันของคุณดี แต่ฉันต้องการให้ผู้คนเข้าใจว่าทำไมจึงต้องใช้แนวทางนี้โดยเริ่มจาก android 4.2
George Dima

2
SPP = Serial Port Profile (ที่เลียนแบบพอร์ตอนุกรมผ่านบลูทู ธ ) และ ELM327 เป็นอุปกรณ์บลูทู ธ <-> obd ในรถยนต์ google it
George Dima

10
มันใช้งานได้สำหรับฉันหลังจากเปลี่ยนค่าพอร์ตจาก 1 เป็น 2 โปรดดูที่รหัสนี้ socket = (BluetoothSocket) device.getClass (). getMethod ("createRfcommSocket", new Class [] {int.class}). invoke (device, 2); socket.connect ();
Dinesh IT

16

ขั้นแรกหากคุณต้องการคุยกับอุปกรณ์บลูทู ธ 2.x เอกสารนี้ระบุว่า:

คำแนะนำ: ถ้าคุณกำลังเชื่อมต่อกับคณะกรรมการอนุกรมบลูทู ธ แล้วลองใช้ที่รู้จักกันดีเอสพีพี UUID 00001101-0000-1000-8000-00805F9B34FB อย่างไรก็ตามหากคุณกำลังเชื่อมต่อกับเพียร์ Android โปรดสร้าง UUID เฉพาะของคุณเอง

ฉันไม่คิดว่ามันจะใช้งานได้ แต่เพียงแค่แทนที่ UUID ด้วย00001101-0000-1000-8000-00805F9B34FBมันเท่านั้น อย่างไรก็ตามรหัสนี้ดูเหมือนจะจัดการกับปัญหาของเวอร์ชัน SDK และคุณสามารถแทนที่ฟังก์ชันdevice.createRfcommSocketToServiceRecord(mMyUuid);ด้วยtmp = createBluetoothSocket(mmDevice);หลังจากกำหนดวิธีการต่อไปนี้:

private BluetoothSocket createBluetoothSocket(BluetoothDevice device)
    throws IOException {
    if(Build.VERSION.SDK_INT >= 10){
        try {
            final Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[] { UUID.class });
            return (BluetoothSocket) m.invoke(device, mMyUuid);
        } catch (Exception e) {
            Log.e(TAG, "Could not create Insecure RFComm Connection",e);
        }
    }
    return  device.createRfcommSocketToServiceRecord(mMyUuid);
}

ซอร์สโค้ดไม่ใช่ของฉัน แต่มาจากเว็บไซต์นี้


1
ที่แก้ไขงานได้เกือบ 2 วัน ... ถอนหายใจขอบคุณ ... หากไม่มี UUID นี้ซ็อกเก็ตจะปิดทันทีและล้มเหลวโดยไม่มีคำอธิบายเพิ่มเติม
David Sinclair

ขอบคุณมากสำหรับความช่วยเหลือ UUID ของฉันเป็นตัวเลขและตัวอักษรและฉันกำลังพยายามเชื่อมต่อ HC-5 และบลูทู ธ แสดงว่าการเชื่อมต่อล้มเหลว แต่เมื่อฉันใช้วิธีนี้แก้ปัญหาของฉัน ขอบคุณอีกครั้ง
Nikhil Shende

8

ฉันมีอาการเช่นเดียวกับที่อธิบายไว้ที่นี่ ฉันสามารถเชื่อมต่อครั้งเดียวกับเครื่องพิมพ์บลูทู ธ แต่การเชื่อมต่อในภายหลังล้มเหลวโดย "ซ็อกเก็ตปิด" ไม่ว่าฉันจะทำอะไร

ฉันพบว่ามันค่อนข้างแปลกที่วิธีแก้ปัญหาที่อธิบายไว้ที่นี่จะเป็นสิ่งที่จำเป็น หลังจากผ่านรหัสของฉันฉันพบว่าฉันลืมปิด InputStream และ OutputSteram ของซ็อกเก็ตและไม่ได้ยุติ ConnectedThreads อย่างถูกต้อง

ConnectedThread ที่ฉันใช้เหมือนกับในตัวอย่างที่นี่:

http://developer.android.com/guide/topics/connectivity/bluetooth.html

โปรดสังเกตว่า ConnectThread และ ConnectedThread เป็นคลาสที่แตกต่างกันสองคลาส

ไม่ว่าคลาสใดก็ตามที่เริ่มต้น ConnectedThread ต้องเรียกใช้ interrupt () และยกเลิก () บนเธรด ฉันเพิ่ม mmInStream.close () และ mmOutStream.close () ในเมธอด ConnectedTread.cancel ()

หลังจากปิดเธรด / สตรีม / ซ็อกเก็ตอย่างถูกต้องฉันสามารถสร้างซ็อกเก็ตใหม่ได้โดยไม่มีปัญหาใด ๆ


ขอบคุณที่มีปัญหาเดียวกันตอนนี้ฉันคิดว่าฉันไม่ได้ปิดสตรีมและการเชื่อมต่อ ...
Rafael

ลองใช้สถานการณ์ข้างต้นทั้งหมด (ยกเว้นการลบอุปกรณ์ที่จับคู่อื่น ๆ ทำให้เกิดปัญหาได้ดีจริง ๆ แล้วพบว่าใช่ ... โดยปกติจะเกิดขึ้นเมื่อสตรีมอินพุตและซ็อกเก็ตไม่ได้ปิดอย่างถูกต้องเท่านั้น ..... !!
Aman Satija

7

ฉันพบปัญหาจริงๆ

คนส่วนใหญ่ที่พยายามที่จะทำให้การเชื่อมต่อใช้จะได้รับการยกเว้นเรียกว่าsocket.Connect();Java.IO.IOException: read failed, socket might closed, read ret: -1

ในบางกรณีอาจขึ้นอยู่กับอุปกรณ์บลูทู ธ ของคุณด้วยเนื่องจากบลูทู ธ มีสองประเภท ได้แก่ BLE (พลังงานต่ำ) และคลาสสิก

หากคุณต้องการตรวจสอบประเภทของอุปกรณ์บลูทู ธ ของคุณนี่คือรหัส:

        String checkType;
        var listDevices = BluetoothAdapter.BondedDevices;
        if (listDevices.Count > 0)
        {
            foreach (var btDevice in listDevices)
            {
                if(btDevice.Name == "MOCUTE-032_B52-CA7E")
                {
                    checkType = btDevice.Type.ToString();
                    Console.WriteLine(checkType);
                }
            }
        }

ฉันพยายามแก้ปัญหามาหลายวันแล้ว แต่ตั้งแต่วันนี้ฉันก็พบปัญหาแล้ว วิธีแก้ปัญหาจาก @matthes ยังคงมีปัญหาเล็กน้อยตามที่เขาพูดไปแล้ว แต่นี่คือวิธีแก้ปัญหาของฉัน

ในขณะนี้ฉันทำงานใน Xamarin Android แต่สิ่งนี้ควรใช้ได้กับแพลตฟอร์มอื่นด้วย

วิธีการแก้

หากมีอุปกรณ์ที่จับคู่มากกว่าหนึ่งเครื่องคุณควรถอดอุปกรณ์ที่จับคู่อื่น ๆ ออก ดังนั้นให้เก็บเฉพาะที่คุณต้องการเชื่อมต่อ (ดูภาพด้านขวา)

ป้อนคำอธิบายภาพที่นี่ ป้อนคำอธิบายภาพที่นี่

ในภาพด้านซ้ายคุณจะเห็นว่าฉันมีอุปกรณ์ที่จับคู่อยู่สองเครื่องคือ "MOCUTE-032_B52-CA7E" และ "Blue Easy" นั่นเป็นปัญหา แต่ฉันไม่รู้ว่าทำไมปัญหาจึงเกิดขึ้น บางทีโปรโตคอลบลูทู ธ กำลังพยายามดึงข้อมูลบางอย่างจากอุปกรณ์บลูทู ธ อื่น

อย่างไรก็ตามsocket.Connect();ตอนนี้ใช้งานได้ดีโดยไม่มีปัญหาใด ๆ ฉันแค่อยากจะแชร์สิ่งนี้เพราะข้อผิดพลาดนั้นน่ารำคาญมาก

โชคดี!


สิ่งนี้ใช้ได้กับอุปกรณ์เดียวที่ฉันพบปัญหานี้ซึ่งเกิดขึ้นกับฉันในโทรศัพท์ 5.0 เท่านั้นและถึงแม้จะเปิดใช้งาน BT เป็นครั้งแรกจากนั้นจึงเปิดการเชื่อมต่อ ถ้า BT เปิดอยู่แล้วไม่มีปัญหาการเชื่อมต่อ! มันไม่ได้เกิดขึ้นบนอุปกรณ์ที่มี Android เวอร์ชันที่ใหม่กว่าโดยบอกว่านักพัฒนาสังเกตเห็นข้อบกพร่องและแก้ไข แต่หน้า Android BT ไม่ได้พูดถึงเรื่องนี้ แต่วิธีแก้ปัญหาของคุณได้ผลขอบคุณ!
John Perry

7

ใน Android เวอร์ชันใหม่กว่าฉันได้รับข้อผิดพลาดนี้เนื่องจากอะแดปเตอร์ยังคงค้นพบเมื่อฉันพยายามเชื่อมต่อกับซ็อกเก็ต แม้ว่าฉันจะเรียกเมธอด CancelDiscovery บนอะแดปเตอร์บลูทู ธ แต่ฉันต้องรอจนกว่าการเรียกกลับไปยังเมธอด onReceive () ของ BroadcastReceiver ถูกเรียกด้วยการกระทำ BluetoothAdapter.ACTION_DISCOVERY_FINISHED

เมื่อฉันรอให้อะแดปเตอร์หยุดการค้นหาแล้วการโทรเชื่อมต่อบนซ็อกเก็ตก็สำเร็จ


6

คุณใส่ registerReceiver(receiver, new IntentFilter("android.bleutooth.device.action.UUID")); ด้วย "บลูทู ธ " ที่สะกดว่า "bleutooth"


5

ในกรณีที่ใครบางคนกำลังมีปัญหากับ Kotlin ฉันต้องทำตามคำตอบที่ยอมรับด้วยรูปแบบต่างๆ:

fun print(view: View, text: String) {
    var adapter = BluetoothAdapter.getDefaultAdapter();
    var pairedDevices = adapter.getBondedDevices()
    var uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
    if (pairedDevices.size > 0) {
        for (device in pairedDevices) {
            var s = device.name
            if (device.getName().equals(printerName, ignoreCase = true)) {
                Thread {
                    var socket = device.createInsecureRfcommSocketToServiceRecord(uuid)
                    var clazz = socket.remoteDevice.javaClass
                    var paramTypes = arrayOf<Class<*>>(Integer.TYPE)
                    var m = clazz.getMethod("createRfcommSocket", *paramTypes)
                    var fallbackSocket = m.invoke(socket.remoteDevice, Integer.valueOf(1)) as BluetoothSocket
                    try {
                        fallbackSocket.connect()
                        var stream = fallbackSocket.outputStream
                        stream.write(text.toByteArray(Charset.forName("UTF-8")))
                    } catch (e: Exception) {
                        e.printStackTrace()
                        Snackbar.make(view, "An error occurred", Snackbar.LENGTH_SHORT).show()
                    }
                }.start()
            }
        }
    }
}

หวังว่าจะช่วยได้


3

อุปกรณ์บลูทู ธ สามารถทำงานได้ทั้งในโหมดคลาสสิกและโหมด LE ในเวลาเดียวกัน บางครั้งพวกเขาใช้ที่อยู่ MAC อื่นขึ้นอยู่กับว่าคุณกำลังเชื่อมต่อด้วยวิธีใด การโทรsocket.connect()ใช้ Bluetooth Classic ดังนั้นคุณต้องตรวจสอบให้แน่ใจว่าอุปกรณ์ที่คุณได้รับเมื่อสแกนเป็นอุปกรณ์คลาสสิกจริงๆ

ง่ายต่อการกรองเฉพาะอุปกรณ์คลาสสิกอย่างไรก็ตาม:

if(BluetoothDevice.DEVICE_TYPE_LE == device.getType()){ //socket.connect() }

หากไม่มีการตรวจสอบนี้เป็นเงื่อนไขการแข่งขันว่าการสแกนแบบไฮบริดจะให้อุปกรณ์ Classic หรืออุปกรณ์ BLE ก่อน อาจดูเหมือนว่าไม่สามารถเชื่อมต่อได้เป็นระยะ ๆ หรือเนื่องจากอุปกรณ์บางอย่างสามารถเชื่อมต่อได้อย่างน่าเชื่อถือในขณะที่อุปกรณ์อื่น ๆ ดูเหมือนจะไม่สามารถทำได้


1

ฉันประสบกับปัญหานี้เช่นกันคุณสามารถแก้ไขได้ 2 วิธีดังที่กล่าวไว้ก่อนหน้านี้ใช้การสะท้อนเพื่อสร้างซ็อกเก็ตที่สองคือไคลเอนต์กำลังมองหาเซิร์ฟเวอร์ที่มี UUID ที่กำหนดและหากเซิร์ฟเวอร์ของคุณไม่ได้ทำงานแบบขนานกับไคลเอนต์สิ่งนี้ เกิดขึ้น สร้างเซิร์ฟเวอร์ด้วย UUID ไคลเอนต์ที่กำหนดจากนั้นฟังและยอมรับไคลเอนต์จากฝั่งเซิร์ฟเวอร์มันจะทำงาน


1

ฉันพบปัญหานี้และแก้ไขโดยการปิดสตรีมอินพุตและเอาต์พุตก่อนปิดซ็อกเก็ต ตอนนี้ฉันสามารถยกเลิกการเชื่อมต่อและเชื่อมต่ออีกครั้งโดยไม่มีปัญหา

https://stackoverflow.com/a/3039807/5688612

ใน Kotlin:

fun disconnect() {
    bluetoothSocket.inputStream.close()
    bluetoothSocket.outputStream.close()
    bluetoothSocket.close()
}

1

หากส่วนอื่นของโค้ดของคุณได้ทำการเชื่อมต่อกับซ็อกเก็ตและ UUID เดียวกันแล้วคุณจะได้รับข้อผิดพลาดนี้


0

แม้ว่าฉันจะมีปัญหาเดียวกัน แต่ในที่สุดก็เข้าใจปัญหาของฉันฉันพยายามเชื่อมต่อจากช่วงครอบคลุมบลูทู ธ (นอกระยะ)


0

ฉันมีปัญหานี้และวิธีแก้ปัญหาคือใช้ GUID มายากลพิเศษ

            val id: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") // Any other GUID doesn't work.
            val device: BluetoothDevice = bta!!.bondedDevices.first { z -> z.name == deviceName }

            bts = device.createRfcommSocketToServiceRecord(id) // mPort is -1
            bts?.connect()
            // Start processing thread.

ฉันสงสัยว่านี่คือ UUID ที่ใช้งานได้:

var did: Array<ParcelUuid?> = device.uuids

อย่างไรก็ตามฉันยังไม่ได้ลองทั้งหมด


แม้ว่าฉันจะใช้ UUID เดียวกันก็ตาม แต่ไม่ได้ช่วยฉัน. รองรับเฉพาะการเชื่อมต่อกับอุปกรณ์บลูทู ธ แบบคลาสสิก (ไม่ใช่ BLE) หรือไม่ สิ่งที่ต้องทำเพื่อเชื่อมต่อกับอุปกรณ์ BLE โดยใช้ Xamarin.Forms โพสต์ที่นี่ [ stackoverflow.com/questions/62371859/…
Shailesh Bhat

ฉันใช้บลูทู ธ แบบคลาสสิก BLE เป็นขยะ
Richard Barraclough

-1

ด้วยการเพิ่มการดำเนินการกรองปัญหาของฉันได้รับการแก้ไข

 // Register for broadcasts when a device is discovered
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
    intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
    intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
    registerReceiver(mReceiver, intentFilter);

-3

ฉันก็ได้รับเช่นเดียวกันIOExceptionแต่ฉันพบว่าการสาธิตระบบ Android: โครงการ "BluetoothChat" ใช้งานได้ ฉันพิจารณาแล้วว่าปัญหาคือ UUID

ดังนั้นฉันจึงเปลี่ยนUUID.fromString("00001001-0000-1000-8000-00805F9B34FB")เป็นUUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66")และมันใช้งานได้เกือบทุกฉากบางครั้งต้องรีสตาร์ทอุปกรณ์บลูทู ธ เท่านั้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.