【问题标题】:Bluetooth transfer App stops after using InputStream.read() with no error蓝牙传输应用程序在使用 InputStream.read() 后停止,没有错误
【发布时间】:2014-11-27 17:37:45
【问题描述】:

我正在尝试使用这些来源使文件传输蓝牙应用程序工作:

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

https://android.googlesource.com/platform/development/+/25b6aed7b2e01ce7bdc0dfa1a79eaf009ad178fe/samples/BluetoothChat/

当我尝试以这种方式使用 InputStream.read() 方法获取 InputStream 字节时:

public class ConnectedThread extends Thread {

...(some code here)

public void run(){

        byte[] buffer = new byte[1024];
        int bytes = -1;

        //Keep listening to the InputStream while connected
        while (true){

            try {

                bytes = this.mmInStream.read(buffer);

                //* this part is not reached
                if (bytes==-1){
                    Log.d("NoData:","-1");  
                }

            }
            catch(Exception e){
                Log.d("inStream exception:",e.getMessage());
                break;
            }

        }

    }

...(some code here)

}

代码的下一部分(在这种情况下为"if" 部分)永远不会到达,也不会出现Log.D 调试输出或我在后面添加的任何其他内容。我刚从 LogCat 收到这条消息:

BluetoothSocket read in: android.net.LocalStocketImpl$SocketInputStream@f7e
                b08 len: 1024

要将数据从客户端传输到服务器,我这样做:

public class MainActivity extends Activity {

...(some code here)

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    clientConnect();
    //serverConnect();

}

...(some code here)

public void clientConnect(){

        Set<BluetoothDevice> devices;

        devices = bConfig.getPairedDevices(); 

        if (devices == null){                   
            return;
        }

        if (devices.size() > 0) {           

            BluetoothDevice device = devices.iterator().next();

            ConnectThread connectTransmit = new ConnectThread(device,bConfig.getBluetoothAdapter(),BluetoothConfig.mUUID);
            connectTransmit.start();

            Toast.makeText(this, "connected", Toast.LENGTH_SHORT).show();

            socket = connectTransmit.mmSocket;
            ConnectedThread connectedThread = new ConnectedThread(socket);

            //write file bytes to the connected thread, so the thread can receive its own input written bytes later
            File file_to_transfer = new File(Environment.getExternalStorageDirectory() + "/txtTransfer.txt");           

            //get bytes from our File
            int size = (int) file_to_transfer.length();
            byte[] bytes = new byte[size];

            try {

                //14b are read succesfully, the whole text file 
                BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file_to_transfer));
                buf.read(bytes,0,bytes.length);
                buf.close();                

            }catch (FileNotFoundException e){
                Log.d("FileNotFoundException:",e.getMessage());
            }catch (IOException e){ 
                Log.d("IOException:",e.getMessage());
            }

            //send the data to the server
            connectedThread.start();
            connectedThread.write(bytes);
            //connectedThread.cancel();

        }

    }

...(some code here)

}

AcceptThread(实现的服务器部分)有效,因为当我运行客户端部分连接然后传输数据时,在设备中调试时,服务器部分上的 LogCat 激活并到达运行方法线程,我在其中调用ConnectedThread 实现,但随后它“显然”读取了字节,但它卡在 LogCat 上而没有错误。

请让我知道我可以做些什么来完成读取字节以移动到流程的下一部分。

谢谢

【问题讨论】:

    标签: java android bluetooth data-transfer


    【解决方案1】:

    您在等待更多输入时被阻止。

    标记为... (some code here) 的部分应在读取循环内,在流结束测试之后。注意如果read() 返回 -1 并不意味着“没有数据”,它意味着流结束,您应该关闭套接字并跳出读取循环。否则,您应该继续处理刚刚读取的数据。目前,您只需阅读并忽略所有输入,直到流结束,这是没有意义的。充其量只能处理最后一个部分缓冲区,并且不知道它持续了多长时间。

    【讨论】:

    • 嗨@EJP,如果应用程序在 mmInStream.read 上被阻止并且未达到 (bytes==-1) 部分,我如何继续将更多输入流式传输到它,以达到该部分和然后用读取的字节跳出循环,所以我终于可以用它们在服务器上写文件了吗?你能写一个我的代码更改的示例脚本吗?非常感谢。
    • 我不知道你在用你正在阅读的数据做什么,因为你还没有发布它,但你需要在读取循环中执行它。
    • 我不确定它是否真的在读取/获取任何数据。脚本的“阅读”部分是我只发布的:bytes = this.mmInStream.read(buffer);但随后发生了停工,不知道下一步该做什么。如果初始连接和写入部分是正确的,并且您可以使用示例脚本将有关如何正确读取字节的解释扩展到服务器端的流末尾(如果可能的话),那将非常有帮助。谢谢。
    • 嗨@EJP,我在这个问题上悬赏,如果你能用一个有效的代码实现示例更好地解释它,我会感谢你。
    • 我已经回答了这一切。发生“停止”是因为没有什么可阅读的了。您需要将数据处理放入循环中。在对等方断开连接之前,循环不会退出。
    【解决方案2】:

    在我看来,您应该在阅读之前验证缓冲区中是否有内容。 从流中读取会阻塞操作,因此应用程序将挂起,直到出现某些数据。 How can I check if an InputStream is empty without reading from it?

    【讨论】:

      【解决方案3】:

      在您的客户端代码中,您可能应该让 connectedThread 对象存活一段时间。可能是一旦 if 子句关闭并且它超出范围(不太确定 GC 和所有会发生什么),写入就不会发生,并且您的连接没有关闭但也没有使用。

      在写入后在 connectedThread 内的 mmOutStream 上调用 flush() 也可能会有所帮助。

      就像@EJP 建议的那样,你应该在你的读取循环中放一些东西。

      编辑:为了调试,您可以在编写客户端代码后立即添加this.wait(1000);

      【讨论】:

      • '再活一段时间'。它一直保持活动状态,直到对等方断开连接,实际上除此之外,因为他没有检查流结束。
      • @EJP 是的,我看到一个未使用且未关闭的连接。仅尝试写入,但是否成功尚不清楚。
      【解决方案4】:

      尝试将您的运行方法更改为:

      public void run(){
          byte[] buffer = new byte[1024];
          int bytesRead = 0;
          final int shortSleepTime = 1000;
          final int longSleepTime = 5000;
          int emptyReadCounter = 0;
          int sleepCounter = 0;
          int currentSleepTime = shortSleepTime;
      
          //Keep listening to the InputStream while connected
          while (bytesRead >= 0){
              try {
      
                  // if available() returns 0, there is nothing to read yet
                  if (this.mmInStream.available() != 0){
                      bytesRead = this.mmInStream.read(buffer);
      
                      // Check if we need to reset the sleep counters
                      if (emptyReadCounter != 0){
                          emptyReadCounter = 0;
                          sleepCounter = 0;
                          currentSleepTime = shortSleepTime;
      
                          // We can also do anything else dependent on just waking up
                          // from a sleep cycle in this block
                      }
      
      
                      // Do something with my now full buffer
                      // Remember not to process more than 
                      // 'bytesRead' bytes from my buffer because the
                      // rest could be filled with crap left over from
                      // the last iteration
                  } else {                
                      // Three consecutive empty reads means sleep
                      if (emptyReadCounter++ >= 3){                       
                          if (currentSleepTime != longSleepTime && sleepCounter++ >= 3){
                              currentSleepTime = longSleepTime;
                          }
                          Thread.sleep(currentSleepTime);
                      }
                  }
              }
              catch(Exception e){
                  Log.d("inStream exception:",e.getMessage());
                  break;
              }
          }
      }
      

      【讨论】:

      • 这会在没有可用数据时抽出 cpu。
      • 这是一个例子。您可以按您认为合适的方式限制它。例如,如果连续 3 次读取都没有得到任何数据,则睡眠一秒钟,如果连续 3 个睡眠周期后没有得到任何数据,则将睡眠时间增加到 5 秒。这样,当有东西进来时,你可以快速地通过它,但是当没有任何东西在等待时,它不会占用你的 CPU。
      • 更新了它以包含我的节流示例。有更有效的方法可以做到这一点,我只是给出一个快速、相当懒惰的例子。
      • 举例说明什么目的? read() 中的阻塞同样有效,或者更有效,因为它不会浪费任何时间,并且更容易编码。他的问题不在于是否阻止,而在于将他的代码放在哪里。显然,他认为read() 将在发件人暂时停止发送时返回,即某种消息边界。您刚刚添加的复杂情况最好通过读取超时来解决。你不需要这一切。你什么都不需要。
      • 不过,在他的问题方案中,这应该不是问题。他正在通过蓝牙传输文件,该方法应该运行的唯一原因是他正在尝试传输文件......而文件不应该被读取的唯一原因是它仍在做准备工作,比如设置开启蓝牙连接。它永远不会工作,因为第一次读取会阻止执行,因此连接不会打开并且读取永远不会成功。
      猜你喜欢
      • 1970-01-01
      • 2017-04-08
      • 1970-01-01
      • 2022-08-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-19
      • 1970-01-01
      相关资源
      最近更新 更多