【问题标题】:InputStream doesn't read via BluetoothInputStream 不通过蓝牙读取
【发布时间】:2013-01-29 15:37:04
【问题描述】:

我有两台设备通过蓝牙连接。我想从一个设备向另一个设备发送更多字符串,但方法 read() 无法正常工作(仅当我想读取最后一个字符串时)。

奇怪的是,它适用于:

inStream.read(keyRead);
outStream.write(answer.getBytes(charsetCod));
inStream.read(sizeRead);
inStream.read(messageRead);
inStream.read(timeSent);

但它仅在应用程序执行时才起作用:

int byteRead = inStream.read(alreadyReceived);

有人可以帮助我理解为什么它不读? (对不起我的英语不好)

这是代码:

当我从 InputStream 中读取时:

public void run(BluetoothDevice device)  {
    Log.i(TAG, "Sono nel run del ConnectedThread");
    dev_recipient = btAdapter.getName();    // se sto per leggere, il destinatario sono io
    dev_sender = device.getName();

    Log.i(TAG, "il mittente è: " + dev_sender);
    Log.i(TAG, "Il destinatario è " + dev_recipient);
    mac_sender = device.getAddress();
    mac_recipient = btAdapter.getAddress();

    byte[] keyRead = new byte[50];
    byte[] sizeRead = new byte[10];
    byte[] timeSent = new byte[100];
    byte[] alreadyReceived = new byte[500];
    String message;
    String sentTime;

    while (true) {                  // finchè è connesso
        try {
            messDB = new MessagesDatabase(context);
            messDB.getWritableDatabase();

            Log.i(TAG, "Destinatario: Ricevo la chiave del messaggio");
            inStream.read(keyRead);
            String keyS = new String(keyRead);
            int indkeyS = keyS.indexOf("#end", 0);
            String key = keyS.substring(0, indkeyS);
            Log.i(TAG, "Stringa chiave del messaggio: " + key);

            Log.i(TAG, "Controllo se ho già ricevuto il messaggio");

            if(messDB.existsMess(key)){
                Log.i(TAG, "Destinatario: Il messaggio è già stato ricevuto");
                Log.i(TAG, "Destinatario: Informa il mittente di non inviare");
                answer = "NO#end";
                outStream.write(answer.getBytes(charsetCod));
                outStream.flush();
                run(device);

            }
            else {
                Log.i(TAG, "Destinatario: Il messaggio non è stato mai ricevuto");
                Log.i(TAG, "Destinatario: Informa il mittente di inviare");
                answer = "YES#end";
                outStream.write(answer.getBytes(charsetCod));
                outStream.flush();

                Log.i(TAG, "Destinatario: Ricevi la grandezza del messaggio");
                inStream.read(sizeRead);
                String s =  new String(sizeRead);
                int ss = s.indexOf("#end", 0);
                Log.i(TAG, "indice di #end: " + ss);
                String sz = s.substring(0, ss);
                Log.i(TAG, sz);
                int size = Integer.parseInt(sz);
                Log.i(TAG, "La grandezza del messaggio è " + size);
                byte[] messageRead = new byte[size];


                Log.i(TAG, "Destinatario: Ricevi il messaggio");
                inStream.read(messageRead);
                message = new String(messageRead);      // converte i bytes in String
                Log.i(TAG, "Ricevuto messaggio dall'inputStream: " + message);

                Log.i(TAG, "Destinatario: ottengo data e ora di ricezione messaggio");
                long receivedTime = System.currentTimeMillis();
                DateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss:SSS");
                Date dt = new Date(receivedTime);
                String receivedTimeString = df.format(dt);
                Log.i(TAG, "Il messaggio è stato ricevuto alle " + receivedTimeString);

                Log.i(TAG, "Destinatario: Ricevo ora e data di invio messaggio");
                inStream.read(timeSent);
                sentTime = new String(timeSent);
                int senInd = sentTime.indexOf("#end", 0);
                String sentTimeSender = sentTime.substring(0, senInd);
                Log.i(TAG, "Il messaggio è stato inviato il: " + sentTimeSender);

                Log.i(TAG, "Destinatario: Ricevo gli already received");
                int byteRead = inStream.read(alreadyReceived);
                Log.i(TAG, "sono stati letti " + byteRead + " byte");
                String ar = new String(alreadyReceived);
                Log.i(TAG, ar);
                int arInd = ar.indexOf("#end", 0);
                String myAr = ar.substring(0, arInd);
                Log.i(TAG, myAr);
                String allReceived = myAr.concat(";mac_recipient");
                Log.i(TAG, "AlreadyReceived: " + allReceived);

                Log.i(TAG, "Destinatario: Informo il mittente che ho ricevuto il messaggio");
                String answOk = "OK#end";
                outStream.write(answOk.getBytes(charsetCod));

                String path = saveMessageAsFile(key, message);

                Log.i(TAG, "Destinatario: Invio l'intent per mostrare all'utente il messaggio ricevuto");
                Intent intent = new Intent(ACTION_MESS_READ);
                intent.putExtra("key_mess", key);
                intent.putExtra("messRead", message);
                intent.putExtra("sender", dev_sender);
                context.sendBroadcast(intent);
                Log.i(TAG, "Intent concluso");

                addMessageToDb(key, message, dev_sender, dev_recipient, receivedTimeString, sentTime, path, allReceived, size);

                read(device);
            }

        }
        catch (IOException e) {
            Log.e(TAG, "Disconnesso!", e);
            connectionLost();
            ManageConnections.this.start();     // riavvia la connessione
            break;
        }
    }   
}

写作:

public void write(final String key, final BluetoothDevice device) {
        dev_sender = btAdapter.getName();
        dev_recipient = device.getName();
        mac_sender = btAdapter.getAddress();
        mac_recipient = device.getAddress();

        final byte[] answerClient = new byte[10]; 
        final byte[] ricevuto = new byte[10];

        try {  
            final String keyy = key.concat("#end");
            final byte[] keyWrite = keyy.getBytes(charsetCod);
            outStream.write(keyWrite);        // invia la chiave del messaggio
            outStream.flush();
            Log.i(TAG, "Mittente: ho inviato la chiave del messaggio al destinatario, aspetto la risposta..");
            try {
                inStream.read(answerClient);
                final String answerC = new String(answerClient);
                Log.i(TAG, answerC);
                final int ansInd = answerC.indexOf("#end", 0);
                Log.i(TAG, "indice: " + ansInd);
                answer = answerC.substring(0, ansInd);
                Log.i(TAG, "Mittente: la risposta del dispositivo: " + answer);

                if (answer == "NO") {
                    Log.i(TAG, "Mittente: Il messaggio è già stato ricevuto, non inviare");
                    aggiornaAlreadyReceived(device.getAddress(), key);

                }
                else {
                    Log.i(TAG, "Mittente: Il messaggio non è stato ricevuto, invia..");
                    messDB = new MessagesDatabase(context);
                    messDB.getWritableDatabase();
                    final Messaggio m = messDB.getMessage(key);

                    Log.i(TAG, "Mittente: Invio la grandezza del messaggio");
                    final int size = m.getSize();
                    Log.i(TAG, "La grandezza è " + size);
                    final String sizeString = String.valueOf(size);
                    final String ss = sizeString.concat("#end");
                    Log.i(TAG, ss);
                    final byte[] sizeByte = ss.getBytes(charsetCod);                        
                    outStream.write(sizeByte);
                    outStream.flush();


                    Log.i(TAG, "Mittente: Invio il messaggio");
                    final byte[] message = m.getMessage().getBytes(charsetCod);
                    final long sentTime = System.currentTimeMillis();
                    outStream.write(message);
                    outStream.flush();

                    Log.i(TAG, "Mittente: Invio data e ora in cui il messaggio è stato mandato");                      
                    final DateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss:SSS");
                    final Date dt = new Date(sentTime);
                    final String sent = df.format(dt);
                    final String sentime = sent.concat("#end");
                    final byte[] sentTimeByte = sentime.getBytes(charsetCod);                      
                    outStream.write(sentTimeByte);
                    outStream.flush();

                    Log.i(TAG, "Mittente: Invio gli already_received");
                    final String myAlRec = m.getAlreadyReceived();
                    Log.i(TAG, myAlRec);
                    final String myAlreadyRec = myAlRec.concat("#end");
                    Log.i(TAG, myAlreadyRec);
                    final byte[] allReceived = myAlreadyRec.getBytes(charsetCod);
                    outStream.write(allReceived);
                    outStream.flush();

                    Log.i(TAG, "Mittente: Aspetto che il destinatario mi informi dell'arrivo del messaggio");
                    String ric = "";
                    inStream.read(ricevuto);
                    final String ricev = new String(ricevuto);
                    Log.i(TAG, "Il destinatario risponde con " + ricev);
                    final int ricInd = ricev.indexOf("#end", 0);
                    ric = ricev.substring(0, ricInd);
                    Log.i(TAG, ric);
                    if(ric == "OK") {
                        Log.i(TAG, "Mittente: Il destinatario ha ricevuto il messaggio");
                        aggiornaAlreadyReceived(mac_recipient, key);
                    }
                    else Log.i(TAG, "Mittente: Il messaggio non è stato ricevuto");

                    Log.i(TAG, "Mittente: Invio l'intent per informare l'utente che il messaggio è stato inviato");
                    final Intent intent = new Intent(ACTION_MESS_WRITE);
                    intent.putExtra("key_mess", key);
                    intent.putExtra("recipient", dev_recipient);
                    intent.putExtra("ric", ric);
                    context.sendBroadcast(intent);
                    Log.i(TAG, "Mittente: Intent concluso");

                }
            }
            catch (final Exception e) {
                Log.e(TAG, "Errore");
                e.printStackTrace();
            }


        }
        catch (final IOException e) {
            Log.e(TAG, "Eccezione durante la scrittura dell'id del messaggio", e);
            e.printStackTrace();
            final Intent intent = new Intent(ACTION_NO_MESSAGE);
            intent.putExtra("key_mess", key);
            context.sendBroadcast(intent);
        }

    }

【问题讨论】:

  • 您能否评论一下您对问题答案的看法?谢谢
  • 我认为有两种方法可以解决这个问题。首先,我可以对“sentTime”数据和“AlreadyReceived”数据使用一次调用。通过这种方式,它工作得很好,但我不明白为什么这个改变解决了这个问题,所以我决定使用 ObjectInputStream,它是一个专门的 InputStream,它能够读取通用 Java 对象,而且使用起来更简单。

标签: android bluetooth inputstream


【解决方案1】:
public class BlueTooth
{
    public String tag = "Bluetooth";
    public static final UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    public BluetoothAdapter adapter;
    public BluetoothDevice device = null;
    public BluetoothSocket socket = null;

    //public static final char ESC = (char) 27;
    //public static final char CR = (char) 13;
    //public static final char LF = (char) 10;

    public BlueTooth(String MAC)
    {
        this(MAC,null);
    }

    @TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1)
    public BlueTooth(String MAC, byte[] pin)
    {
        try
        {
            adapter = BluetoothAdapter.getDefaultAdapter();
            // wait for enable this is not very correct
            if (!adapter.isEnabled()) {
                adapter.enable();
                SystemClock.sleep(5000);
                if (!adapter.isEnabled()) throw new IOException("Bluetooth is off");
            }

            if (!BluetoothAdapter.checkBluetoothAddress(MAC)) throw new Exception("Err MAC adress (" + MAC + ")");

            device = adapter.getRemoteDevice(MAC);
            // this is only for testing purpouse i dont use this
            if (pin!=null)
                setPin(pin);
            // i have some trouble with my device on different OS                
            if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.HONEYCOMB)
                socket = device.createInsecureRfcommSocketToServiceRecord(uuid);
            else
                socket = device.createRfcommSocketToServiceRecord(uuid);

        }
        catch (Exception e)
        {
            throw new RuntimeException(e);
        }
        finally
        {

        }
    }

    public void setPin(byte[] pin)
    {
        try
        {
            Method setPin = device.getClass().getMethod("setPin", byte[].class);
            setPin.invoke(device, pin);
            Log.i("BlueTooth", "PIN is set: "+new String(pin));
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    @Override
    protected void finalize() throws Throwable
    {
        disconnect();
        super.finalize();
    }

    // Android REfs recommend to cancel discover
    public boolean connect()
    {
        try
        {
            while (adapter.isDiscovering())
            {
                adapter.cancelDiscovery();
                Thread.sleep(1000);
            }

            socket.connect();
            return true;
        }
        catch(Exception e)
        {
            throw new RuntimeException(e.getMessage(),e);
        }
    }

    // After write is good(only for shure for next write) to clear buffer with flush    
    public void write(byte[] in) throws Exception
    {
        socket.getOutputStream().write(in, 0, in.length);
        socket.getOutputStream().flush();
    }


    // Wait for incomming data
    // if is something in the buffer start count but max 3sec 
    // 3 sec is defined by my device vendor for reply
    //
    // after it we something on buffer.
    // you must realize the reading is on the fly
    // there for you must wait after read to next data sleep(1mxec)
    // until data are available
    // then return
    public byte[] read() throws Exception
    {

        int treshHold = 0;
        while (socket.getInputStream().available()==0 && treshHold<3000)
        {
            Thread.sleep(1);            
            treshHold++;
        }

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.reset();

        while (socket.getInputStream().available() > 0)
        {            
            baos.write(socket.getInputStream().read());
            Thread.sleep(1);   
        }

        return baos.toByteArray();

    }

    // This is little low
    // after disconnect you must create 
    // new socket
    // so after disconnect you are not able to use connect
    // withous createRFcommSocket
    public void disconnect() throws Exception
    {
        if (socket != null) socket.close();
    }
}

【讨论】:

  • 嘿,Mertuarez,干得漂亮;)您能否添加一些解释。尤其是你使用 Thread.sleep() 的部分
【解决方案2】:

不能说我完全理解您的代码,但有一些提示可能会有所帮助。

请查看Managing a Connection 部分。如您所见;

您应该使用 专用线程 进行所有流读取和 写作。这很重要,因为 read(byte[])write(byte[]) 方法是阻塞调用read(byte[]) 将阻塞 直到有 从流中读取的东西write(byte[]) 通常不会 阻止,但如果远程设备不是,则可以阻止流量控制 足够快地调用 read(byte[]) 并且中间缓冲区是 满的。所以,你在线程中的主循环应该专门用于阅读 来自 InputStream。

首先,为了获得更好的设计,您应该使用一个专用线程来读取,而另一个线程用于写入。您应该将形成数据的部分分开,将其写入 OutputStream 并从 InputStream 中读取。

当我开始编写我的 Reader Thread 时,InputStream 的 read(byte[]) 方法将阻塞执行,直到有所需的字节数。但事实并非如此。我发现它确实会阻塞,直到从流中读取“某些东西”(我之前错过的部分)。


您有一个大小为 500 的字节数组。当您“不能”(在这种情况下)部分读取流时,日志中的条目是什么:

int byteRead = inStream.read(alreadyReceived);
Log.i(TAG, "sono stati" + byteRead + " byte");

我的意思是:考虑你的包裹是; 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09,您通过 OutputStream 写入发送它并刷新。您仍然可能在一次 read() 调用中获得其中的一部分(甚至 1 个字节 -> 0x01),并在下一次 read() 调用中获得其余部分(0x02 和其余部分) .在这种情况下,byteRead 将等于 1,alreadyReceived[0] 将是 0x01,其他元素将为 0

【讨论】:

  • 对不起,我忘了说我使用了不同的线程进行阅读和写作。我只发布了两个函数(运行和写入)。部分代码为:Log.i(TAG, "Destinatario: Ricevo gli already received"); int byteRead = inStream.read(alreadyReceived); Log.i(TAG, "sono stati" + byteRead + "byte");日志打印:Destinatario:Ricevo gli 已经收到。没有别的了。它阻塞在 inStream.read(alreadyReceived);好像没有别的东西可以读,但我真的不明白为什么。谢谢你的回答!
  • 您确定在 InputStream 中还有其他字节要读取吗?我建议您使用一个大数组对其进行测试,并在一个方法调用中读取()所有数据。您也可以使用 InputStream.available() 方法,但正如文档所述,它不是很可靠。您可以使用它来检查流中是否有可用的数据,检查返回值是否为 0。 (正如 Mertuarez 的代码中所做的那样)
  • 我已经解决了!正如您建议我的那样,我对“sentTime”数据和“AlreadyReceived”数据使用了一次调用。所以,我使用了一个新的 String a = sentTime.concat(AlreadyReceived),然后是 inStream.read(a)。这样,它工作正常。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-02
  • 2016-03-30
  • 1970-01-01
  • 2014-10-16
  • 2015-12-25
  • 2022-12-11
相关资源
最近更新 更多