InputStream不通过蓝牙接收数据

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了InputStream不通过蓝牙接收数据相关的知识,希望对你有一定的参考价值。

编辑1:MCVE-

我让我的整个代码都在主要问题中,但是因为我被要求提供MCVE-

设备未接收其他连接的android设备发送给它的数据。代码未运行“inputStream.read(buffer)”,因为它没有接收数据。

发送数据的代码:

public void sendData(String s) throws IOException{
    byte[] byteString=s.getBytes();
    outputStream.write(byteString);
    outputStream.flush();
    Toast.makeText(getApplicationContext(), "Message sent", Toast.LENGTH_SHORT).show();
}

接收:

while (true){
            inputStream.read(buffer);
            final String str=new String(buffer);
            try{
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, str, Toast.LENGTH_SHORT).show();
                    }
                });
            }catch (Exception e){
                Toast.makeText(context, "Error in reading characters", Toast.LENGTH_SHORT).show();
            }
        }

另外,我这样连接了我的套接字:

Method method = bluetoothDevice.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
bluetoothSocket = (BluetoothSocket) method.invoke(bluetoothDevice, 2);

使用端口号1或createRfCcommSocketToServiceRecord无法正常工作,因为连接失败。

整个问题:

我正在开发一个应用程序,我需要一个功能,通过蓝牙提供两个Android设备之间的双向通信。发送的数据将是简单的字符串。

我能够正确连接两个设备。这是我的代码:

 bluetoothAdapter=BluetoothAdapter.getDefaultAdapter();
    if(!bluetoothAdapter.isEnabled()){
        Intent intent= new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(intent,1);
    }

    pairedDevices=bluetoothAdapter.getBondedDevices();
    List<String> pairedDevicesList=new ArrayList<String>();
    if (pairedDevices.size() > 0) {
        for (BluetoothDevice device : pairedDevices) {
            pairedDevicesList.add(device.toString());
        }
    }

    listView.setVisibility(View.GONE);
    btListView.setVisibility(View.VISIBLE);
    btListView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, pairedDevicesList));

这将显示ListView上的所有配对设备。现在,在选择任何这些设备时,将使用以下连接进行连接:

try{
        btListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                try {
                    BluetoothDevice pairedDevicesArray[] = pairedDevices.toArray(new BluetoothDevice[pairedDevices.size()]);
                    bluetoothDevice = pairedDevicesArray[position];
                } catch (Exception e) {
                    Toast.makeText(getApplicationContext(), "Error in getting BT Device", Toast.LENGTH_SHORT).show();
                }

                try {
                    ParcelUuid parcelUuidArray[];
                    List<UUID> uuidList = new ArrayList<UUID>();
                    Class cl = Class.forName("android.bluetooth.BluetoothDevice");
                    Class[] params = {};
                    Method method = cl.getMethod("getUuids", params);
                    Object[] args = {};
                    parcelUuidArray = (ParcelUuid[]) method.invoke(bluetoothDevice, args);
                    for (ParcelUuid u : parcelUuidArray) {
                        uuidList.add(u.getUuid());
                    }
                    uuid = uuidList.get(0);
                    Toast.makeText(getApplicationContext(), uuid.toString(), Toast.LENGTH_SHORT).show();
                } catch (Exception e) {
                    Toast.makeText(getApplicationContext(), "Error in getting UUIDs", Toast.LENGTH_SHORT).show();
                }


                try {
                    Method method = bluetoothDevice.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
                    bluetoothSocket = (BluetoothSocket) method.invoke(bluetoothDevice, 2);
                    //bluetoothSocket = bluetoothDevice.createRfcommSocketToServiceRecord(uuid);
                    Toast.makeText(getApplicationContext(), bluetoothSocket.toString(), Toast.LENGTH_SHORT).show();
                } catch (Exception e) {
                    Toast.makeText(getApplicationContext(), "Error in getting BT Socket", Toast.LENGTH_SHORT).show();
                }

                try {
                    bluetoothAdapter.cancelDiscovery();
                    if (!bluetoothSocket.isConnected()) {
                        bluetoothSocket.connect();
                    }
                    Toast.makeText(getApplicationContext(), "CONNECTION SUCCESSFUL!", Toast.LENGTH_SHORT).show();
                } catch (Exception e) {
                    Toast.makeText(getApplicationContext(), "Error in connecting", Toast.LENGTH_SHORT).show();
                }
                btListView.setVisibility(View.GONE);
                listView.setVisibility(View.VISIBLE);

                try {
                    outputStream = bluetoothSocket.getOutputStream();
                    inputStream = bluetoothSocket.getInputStream();
                    Toast.makeText(getApplicationContext(), "Streams retrieved", Toast.LENGTH_SHORT).show();
                    //listenForData();
                } catch (Exception e) {
                    Toast.makeText(getApplicationContext(), "Error in getting streams", Toast.LENGTH_SHORT).show();
                }



                final Handler handler = new Handler();
                try {
                    BluetoothSocketListener bluetoothSocketListener = new BluetoothSocketListener(bluetoothSocket, handler, getApplicationContext());
                    Thread newThread = new Thread(bluetoothSocketListener);
                    newThread.start();
                    Toast.makeText(getApplicationContext(), "New Thread Running", Toast.LENGTH_SHORT).show();
                }catch(Exception e){
                    Toast.makeText(getApplicationContext(), "Error in new thread", Toast.LENGTH_SHORT).show();
                }

            }
        });
    }catch (Exception e){
        Toast.makeText(getApplicationContext(), "Error in 2nd listview", Toast.LENGTH_SHORT).show();
    }

此代码块末尾的Handler创建另一个Thread,它继续运行以接收其他设备发送的数据。这是该线程的代码:

public class BluetoothSocketListener implements Runnable {

private BluetoothSocket bluetoothSocket;
private Handler handler;
Context context;
public BluetoothSocketListener(BluetoothSocket socket, Handler handler, Context c){
    this.bluetoothSocket=socket;
    this.handler=handler;
    this.context=c;
}

@Override
public void run(){
    int bufferSize=1024;
    final byte[] buffer=new byte[bufferSize];
    try {
        final InputStream inputStream = bluetoothSocket.getInputStream();
        int bytesRead = -1;
        String message = "";
        handler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(context, "Listening for data with Stream: "+inputStream.toString(), Toast.LENGTH_SHORT).show();
            }
        });
        while (true){
            bytesRead= inputStream.read(buffer);
            final String str=new String(buffer);
            try{
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, str, Toast.LENGTH_SHORT).show();
                    }
                });
            }catch (Exception e){
                Toast.makeText(context, "Error in reading characters", Toast.LENGTH_SHORT).show();
            }
        }

    }catch(Exception e){
        handler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(context, "Error in listening to data", Toast.LENGTH_SHORT).show();
            }
        });
    }
}}

并且,在MainActivity类中具有此功能的任何设备上的数据都被写入OutputStream:

public void sendData(String s) throws IOException{
    byte[] byteString=s.getBytes();
    outputStream.write(byteString);
    outputStream.flush();
    Toast.makeText(getApplicationContext(), "Message sent", Toast.LENGTH_SHORT).show();
}

String s是需要发送的字符串。

在同时在两台独立设备上运行我的应用程序(一台设备安装了Android 5.1.1,另一台设备安装了Android 6.0)时,设备在两部手机上的配对设备列表中以及相互之间的连接方式如何。

显示所有toast,包括“New Thread Running”和“Listening For Data”,因此代码运行正常。在尝试发送任何内容时,“已发送消息”的吐司也会显示出来。但是没有收到数据(代码没有通过inputStream.read(缓冲区)运行作为测试Toast,它在它没有显示后放了)。

这意味着它正在进行到那一点并等待从InputSream中读取永远不会出现的数据,即使它已成功写入另一个设备上的OutputStream。

关闭应用程序后,将显示错误消息“侦听数据时出错”,证明第二个线程始终正常运行。

任何人都可以告诉我哪里出错了?我的设备是从一个插槽查看InputStream,而第二个设备将数据发送到另一个插槽。如果是这样,我怎么能确保它听到正确的套接字?

我查看了StackOverflow上的所有类似问题,甚至检查了Google的蓝牙聊天应用程序示例,但找不到任何解决方案。我尝试了不同的方式发送字符串(使用OutputStreamWriting并以UTF-8编码等等) )并接收它(尝试逐个字符地接收数据,或者在InputStream上使用BufferedReader),但每次都有完全相同的问题。应用程序的其余部分工作正常。

谢谢。

答案

我为第一篇文章道歉。这是一个答案,如果我正确理解蓝牙你的run()函数在CONNECTING阶段和CONNECTED之前启动。 read函数从不完整的连接开始。

@Override
public void run(){
    int bufferSize=1024;
    final byte[] buffer=new byte[bufferSize];
    try {
        final InputStream inputStream = bluetoothSocket.getInputStream();
        int bytesRead = -1;
        String message = "";
        handler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(context, "Listening for data with Stream: "+inputStream.toString(), Toast.LENGTH_SHORT).show();
            }
        });
        while (true){
            bytesRead= inputStream.read(buffer);
            final String str=new String(buffer);

具体来说,解决您对聊天示例的引用以及您编辑某些代码的相似之处。问题部分是由于你使用“Toast”而不是Log.d API,因为图形需要很长时间。

// Constants that indicate the current connection state
public static final int STATE_NONE = 0;       // we're doing nothing
public static final int STATE_LISTEN = 1;     // now listening for incoming connections
public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
public static final int STATE_CONNECTED = 3;  // now connected to a remote device

/** Constructor. Prepares a new BluetoothChat session.
 *
 * @param context The UI Activity Context
 * @param handler A Handler to send messages back to the UI Activity
 */
public BluetoothSPP(Context context, Handler handler) {
    mAdapter = BluetoothAdapter.getDefaultAdapter();
    mState = STATE_NONE;
    mHandler = handler;
}

/** Set the current state of the chat connection
 *
 * @param state An integer defining the current connection state
 */
private synchronized void setState(int state) {
    Log.v(TAG, "BT State changed " + mState + " -> " + state);
    mState = state;

    // Give the new state to the Handler so the UI Activity can update
    mHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
}

观察mConnectedThread.start()和setState(STATE_CONNECTED)之间的消息处理程序

public synchronized void connected(BluetoothSocket socket, BluetoothDevice
        device, final String socketType) {
    Log.d(TAG, "BT connected, Socket Type:" + socketType);

    // Cancel the thread that completed the connection
    if (mConnectThread != null) {
        mConnectThread.cancel();
        mConnectThread = null;
    }

    // Cancel any thread currently running a connection
    if (mConnectedThread != null) {
        mConnectedThread.cancel();
        mConnectedThread = null;
    }

    // Cancel the accept thread because we only want to connect to one device
    if (mSecureAcceptThread != null) {
        mSecureAcceptThread.cancel();
        mSecureAcceptThread = null;
    }
    if (mInsecureAcceptThread != null) {
        mInsecureAcceptThread.cancel();
        mInsecureAcceptThread = null;
    }

    // Start the thread to manage the connection and perform transmissions
    mConnectedThread = new ConnectedThread(socket, socketType);
    mConnectedThread.start();

    // Send the name of the connected device back to the UI Activity
    Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME);
    Bundle bundle = new Bundle();
    bundle.putString(Constants.DEVICE_NAME, device.getName());
    msg.setData(bundle);
    mHandler.sendMessage(msg);

    setState(STATE_CONNECTED);
}

这是红线。

private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;

    public ConnectedThread(BluetoothSocket socket, String socketType) {
        Log.v(TAG, "BT Connected: " + socketType);
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;

        // Get the BluetoothSocket input and output streams
        try {
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } catch (IOException e) {
            Log.e(TAG, "BT sockets not created", e);
        }

        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }

    public void run() {
        Log.i(TAG, "BEGIN BT Monitor");
        byte[] buffer = new byte[1024];
        int bytes;

        // Keep listening to the InputStream while connected
        while (mState == STATE_CONNECTED) {
            try {
                // Read from the InputStream
                bytes = mmInStream.read(buffer);

                // Send the obtained bytes to the UI Activity
                mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
                        .sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "BT Monitor Disconnected", e);
                connectionLost();
                // Start the service over to restart listening mode
                BluetoothSPP.this.start();
                break;
            }
        }
        Log.i(TAG, "End BT Monitor");

    }

Android Studio logcat中的输出如下所示。它从未进入while(mState == STATE_CONNECTED)循环。

Output of Android Studio logcat during code execution

我通过添加一个处理连接状态的循环来纠正代码。

public void run() {
        Log.i(TAG, "BEGIN BT Monitor");
        byte[] buffer = new byte[1024];
        int bytes;

        while (mState == STATE_CONNECTING){
            Log.i(TAG, "BT Monitor Paused");
        }
        // Keep listening to the InputStream while connected
        while (mState == STATE_CONNECTED) {
            try {
                // Read from the InputStream
                bytes = mmInStream.read(buffer);

                // Send the obtained bytes to the UI Activity
                mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
                        .sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "BT Monitor Disconnected", e);
                connectionLost();
                // Start the service over to restart listening mode
                BluetoothSPP.this.start();
                break;
            }
        }
        Log.i(TAG, "End BT Monitor");

    }

logcat输出显示“STATE_CONNECTING”循环,该循环在表示连接已完成的日志消息之前发生15次,并且在代码执行之后发生一次(显示时间依赖性)Toast和其他消息。

Android Studio logcat output showing code execution

以上是关于InputStream不通过蓝牙接收数据的主要内容,如果未能解决你的问题,请参考以下文章

安卓开发蓝牙接收数据,返回数据如何处理

Android蓝牙输入/输出流:发送~1400字节,接收1008

如何使用android蓝牙接收串行数据

在Android上的蓝牙中从InputStream读取数据时出错

向远程蓝牙设备发送数据

如果没有发送任何内容,Stream.Read不会返回任何内容