2015-05-16 21 views
8

Tôi cần truyền một số dữ liệu từ ứng dụng Android của mình qua Bluetooth (sang Arduino). Tôi không đọc/nhận lại bất cứ điều gì từ Arduino. Đối với các nhu cầu đơn luồng của tôi, tôi đã đi với một IntentService. Sau khi ghép nối, mã của tôi hoạt động tốt cho lần đầu tiên tôi kết nối và gửi dữ liệu. Tôi ngắt kết nối sau khi gửi dữ liệu mà không có lỗi. Nhưng khi tôi cố gắng kết nối lần thứ hai trở đi, tôi nhận được lỗi sau khi tôi cố gắng myBluetoothSocket.connect():Sự cố kết nối Bluetooth với IntentService

đọc thất bại, ổ cắm có thể đóng cửa hoặc thời gian chờ, đọc ret: -1

Chỉ có giải pháp là tắt nguồn thiết bị Arduino và kết nối lại (nó không giúp được nếu tôi buộc dừng ứng dụng và thử kết nối lại).

Lưu ý rằng mọi thứ hoạt động tốt nếu tôi sinh ra 2 luồng (mỗi lần đọc và ghi) bất kể số lần tôi kết nối và gửi dữ liệu (do đó chứng minh không có gì sai ở phía Arduino, "giữ lại" cũ kết nối).

Đây là mã Android của tôi:

import android.app.IntentService; 
import android.bluetooth.BluetoothAdapter; 
import android.bluetooth.BluetoothDevice; 
import android.bluetooth.BluetoothSocket; 
import android.content.Intent; 
import android.content.Context; 
import android.os.Build; 
import android.os.ParcelUuid; 
import android.widget.Toast; 

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

public class DataTransmissionService extends IntentService { 

    private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); 
    private static final String TAG = "DataTransmissionService"; 

    private BluetoothAdapter btAdapter = null; 
    private BluetoothSocket btSocket = null; 
    private OutputStream outStream = null; 
    private BluetoothDevice device = null; 

    public DataTransmissionService() { 
     super("DataTransmissionService"); 
    } 

    @Override 
    protected void onHandleIntent(Intent intent) { 
     cleanup(); 
     if (intent != null){ 

      btAdapter = BluetoothAdapter.getDefaultAdapter(); 
      pairedDeviceAddress = "already_paired_device_mac_addr"; 

      try { 
       log.d(TAG, pairedDeviceAddress); 
       device = btAdapter.getRemoteDevice(pairedDeviceAddress); 
       log.d(TAG, "Device bond state : " + device.getBondState()); 

      } catch (Exception e) { 
       log.e(TAG, "Invalid address: " + e.getMessage()); 
       return; 
      } 

      try { 
       btSocket = createBluetoothSocket(device); 
      } catch (IOException e) { 
       log.e(TAG, "Socket creation failed: " + e.getMessage()); 
       return; 
      } 

      try { 

       if (!btSocket.isConnected()) { 
        btSocket.connect();  
        log.d(TAG, "Connected"); 
       } else { 
        log.d(TAG, "Already Connected"); //flow never reaches here for any use case 
       } 

      } catch (IOException e) { 
       log.e(TAG, "btSocket.connect() failed : " + e.getMessage()); 
       return; 
      } 

      try { 
       outStream = btSocket.getOutputStream(); 
      } catch (IOException e) { 
       log.e(TAG, "Failed to get output stream:" + e.getMessage()); 
       return; 
      } 

      sendData("test"); 
      //cleanup(); called in onDestroy() 

     } 

    } 

    @Override 
    public void onDestroy(){ 
     cleanup(); 
     //notify ui 
     super.onDestroy(); 
    } 

    private void cleanup(){ 

     try { 
      if (outStream != null) { 
       outStream.close(); 
       outStream = null; 
      } 
     } catch (Exception e) { 
      log.e(TAG, "Failed to close output stream : " + e.getMessage()); 
     } 

     try { 
      if (btSocket != null) { 
       btSocket.close(); 
       btSocket = null; 
      } 
     }catch (Exception e) { 
      log.e(TAG, "Failed to close connection : " + e.getMessage()); 
     } 

    } 

    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, MY_UUID); 
      } catch (Exception e) { 
       log.e(TAG, "Could not create Insecure RFComm Connection",e); 
      } 
     }*/ 

     return device.createRfcommSocketToServiceRecord(MY_UUID); 
    } 

    private void sendData(String message) { 

     byte[] msgBuffer = message.getBytes(); 
     log.d(TAG, "Sending : " + message); 
     try { 
      outStream.write(msgBuffer); 
     } catch (IOException e) { 
      log.e(TAG, "failed to write " + message); 
     } 
    } 
} 

tôi đã thử nghiệm trên Nexus 5 và các thiết bị Samsung S5 (chạy 5.1 và 5.0 tương ứng).

+0

Tôi có một cảm giác vấn đề của bạn nằm trong việc sử dụng một 'IntentService' chứ không phải là một' Service' và/hoặc tùy chỉnh 'Chủ đề bình thường '. Từ tài liệu 'IntentService': * dịch vụ được khởi động khi cần thiết, xử lý từng Intent lần lượt bằng cách sử dụng một chuỗi công nhân, và dừng lại khi nó hết công việc. * Điều này có thể gây ra sự tàn phá nếu bạn đang cố gắng duy trì một BT kết nối giữa các mục đích. – pathfinderelite

+0

Đó chính xác là những gì tôi cần. Kết nối, gửi dữ liệu và chấm dứt kết nối. Kết nối tiếp theo có thể sẽ xảy ra sau rất lâu, sinh ra một dịch vụ mới. Duy trì chủ đề có vẻ cồng kềnh cho một trường hợp sử dụng đơn giản như thế này. Điều này cũng tự động đảm bảo việc xử lý một thiết bị tại một thời điểm (một phần trong trường hợp sử dụng của tôi). – SlowAndSteady

+0

Những gì bạn nói về Arduino không "giữ lại" kết nối có vẻ hợp lý, nhưng thực tế là bạn phải tắt Arduino và bật để có thể kết nối lại khiến tôi ngạc nhiên. Có lẽ Arduino bằng cách nào đó đi vào trạng thái khác nếu bạn đọc và viết trên cùng một luồng? (Tôi biết, có vẻ kỳ quặc.) Điều gì sẽ xảy ra nếu bạn kết nối với Arduino bằng một điện thoại, ngắt kết nối, sau đó thử kết nối với điện thoại thứ 2? Ngoài ra, bạn có bất kỳ cách nào để xem trạng thái của modem bluetooth của Arduino, để xem nếu nó bằng cách nào đó khác nhau sau khi bạn ngắt kết nối lần đầu tiên, so với trước đó? – hBrent

Trả lời

0

Tôi không chắc chắn lý do tại sao nó hoạt động, nhưng phương pháp này cuối cùng làm việc:

private BluetoothSocket createBluetoothSocket(BluetoothDevice bluetoothDevice) throws IOException { 

     try { 
      Method m = bluetoothDevice.getClass().getMethod(
        "createRfcommSocket", new Class[] { int.class }); 
      btSocket = (BluetoothSocket) m.invoke(bluetoothDevice, 1); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     return btSocket; 
    } 
2

Khi bạn cố gắng kết nối lần thứ hai, bạn phải tạo lại ổ cắm tương ứng.

Ngoài ra, bạn phải xem xét Arduino là nền tảng chậm, có thể có một số chậm trễ đáng kể giữa việc đóng kết nối và bạn có thể mở lại.

+0

Mã tôi đã đăng đang tạo một ổ cắm mới mọi lúc. Bạn không đồng ý – SlowAndSteady

-1

Phương thức onDestroy() chỉ được gọi khi bộ thu gom rác chạy. Bạn cần gọi số cleanup() từ số onHandleIntent(Intent) như bạn đã làm trước khi không, ổ cắm sẽ vẫn mở vô thời hạn. Vì bạn đã mở nó, bạn không thể kết nối lại.

Ngăn xếp Bluetooth của Android dường như không thuyết phục được vòng đời của ứng dụng: ổ cắm sẽ vẫn mở ngay cả khi bạn buộc dừng ứng dụng. Trong kịch bản hiện tại của bạn, để đóng socket, hãy tắt chức năng bật Bluetooth trong Cài đặt.

Các vấn đề liên quan