2014-11-05 49 views
1

我正在嘗試獲取自定義設備的通知。我知道設備正在工作,因爲在iOS中使用Lightblue應用程序時,我收到了通知。我已經嘗試了一個nexus 5和一個三星s4(都kitkats)。似乎沒有任何工作。我的「onCharacteristicChanged」沒有被調用。如何在android中獲取BLE通知?

MainActivity.java

package com.foo.ble; 

import android.app.Activity; 
import android.bluetooth.BluetoothAdapter; 
import android.bluetooth.BluetoothDevice; 
import android.bluetooth.BluetoothManager; 
import android.content.Context; 
import android.os.Bundle; 
import android.os.Handler; 
import android.util.Log; 


public class MainActivity extends Activity implements BluetoothAdapter.LeScanCallback { 
    private BluetoothAdapter adapter; 

    final private static char[] hexArray = "ABCDEF".toCharArray(); 
    private Callback callback; 

    /** 
    * Converts 0xfe to "FE" 
    * @return hex representation of the adScanned 
    */ 
    public static String getPayload(final byte[] adScanned) { 
     if (adScanned == null) return "N/A"; 

     final char[] hexChars = new char[adScanned.length * 2]; 
     for (int j = 0; j < adScanned.length; j++) { 
      final int v = adScanned[j] & 0xFF; 
      hexChars[j * 2] = hexArray[v >>> 4]; 
      hexChars[j * 2 + 1] = hexArray[v & 0x0F]; 
     } 
     return new String(hexChars); 
    } 

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

     BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); 
     adapter = manager.getAdapter(); 
     callback = new Callback(); 

     Handler handler = new Handler(); 
     handler.postDelayed(new Runnable() { 
      @Override 
      public void run() { 
       adapter.stopLeScan(MainActivity.this); 
      } 
     }, 3*10000); 

     runOnUiThread(new Runnable() { 
      @Override 
      public void run() { 
       adapter.startLeScan(MainActivity.this); 
      } 
     }); 
    } 

    @Override 
    public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) { 
     if (device.getAddress().equals("CE:AD:09:F2:BB:DC")) { 
      Log.d("foo", "onLeScan payload:" + getPayload(scanRecord)); 
      adapter.stopLeScan(this); 
      runOnUiThread(new Runnable() { 
       @Override 
       public void run() { 
        device.connectGatt(MainActivity.this, true, callback); 
       } 
      }); 
     } 
    } 

    @Override 
    protected void onDestroy() { 
     callback.close(); 
    } 
} 

Callback.java

package com.foo.ble; 

import android.bluetooth.BluetoothGatt; 
import android.bluetooth.BluetoothGattCallback; 
import android.bluetooth.BluetoothGattCharacteristic; 
import android.bluetooth.BluetoothGattDescriptor; 
import android.bluetooth.BluetoothProfile; 
import android.util.Log; 

import java.util.UUID; 

public class Callback extends BluetoothGattCallback { 
    final static UUID SERVICE = UUID.fromString("something1"); 
    final static UUID ADV_CHAR = UUID.fromString("something2"); 
    final static UUID ADV_DESCRIPTOR = UUID.fromString("something3"); 

    BluetoothGatt gatt; 

    public void close() { 
     if (gatt == null) return; 
     Log.d("foo", "close with gatt"); 

     gatt.disconnect(); 
     gatt.close(); 
     gatt = null; 
    } 

    @Override 
    public void onConnectionStateChange(final BluetoothGatt gatt, final int status, 
             final int newState) { 
     Log.d("foo", "onConnectionStateChange"); 
     if (newState == BluetoothProfile.STATE_CONNECTED) { 
      Log.d("foo", "status connected"); 
      this.gatt = gatt; 
      gatt.discoverServices(); 
     } 
    } 

    @Override 
    public void onServicesDiscovered(final BluetoothGatt gatt, final int status) { 
     Log.d("foo", "onServicesDiscovered"); 
     if (status == BluetoothGatt.GATT_SUCCESS) { 
      final BluetoothGattCharacteristic characteristic = 
        gatt.getService(SERVICE).getCharacteristic(ADV_CHAR); 
      final BluetoothGattDescriptor descriptor = 
        characteristic.getDescriptor(ADV_DESCRIPTOR); 
      descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 
      gatt.setCharacteristicNotification(characteristic, true); 
      gatt.writeDescriptor(descriptor); 
     } 
    } 

    @Override 
    public void onCharacteristicRead(final BluetoothGatt gatt, 
            final BluetoothGattCharacteristic characteristic, 
            final int status) { 
     Log.d("foo", "onCharacteristicRead status:" + status + " payload: " + 
       MainActivity.getPayload(characteristic.getValue())); 
    } 

    @Override 
    public void onCharacteristicWrite(final BluetoothGatt gatt, 
             final BluetoothGattCharacteristic characteristic, 
             final int status) { 
     Log.d("foo", "onCharacteristicWrite"); 
    } 

    @Override 
    public void onCharacteristicChanged(final BluetoothGatt gatt, 
             final BluetoothGattCharacteristic characteristic) { 
     Log.d("foo", "onCharacteristicChanged payload:" + 
       MainActivity.getPayload(characteristic.getValue())); 
    } 

    @Override 
    public void onDescriptorWrite(final BluetoothGatt gatt, 
            final BluetoothGattDescriptor descriptor, 
            final int status) { 
     Log.d("foo", "onDescriptorWrite status: " + status); 
     final BluetoothGattCharacteristic characteristic = 
       gatt.getService(SERVICE).getCharacteristic(ADV_CHAR); 
     if (characteristic.getValue() != null) { 
      Log.d("foo", "value: " + MainActivity.getPayload(characteristic.getValue())); 
     } else { 
      gatt.readCharacteristic(characteristic); 
     } 
    } 

} 

我的AndroidManifest.xml有:

<uses-permission android:name="android.permission.BLUETOOTH" /> 
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> 

日誌看起來是這樣的:

11-05 12:17:13.314 3683-3683/com.foo.ble D/BluetoothAdapter﹕ startLeScan(): null 
11-05 12:17:13.364 3683-3695/com.foo.ble D/BluetoothAdapter﹕ onClientRegistered() - status=0 clientIf=5 
11-05 12:17:13.394 3683-3683/com.foo.ble I/Adreno-EGL﹕ <qeglDrvAPI_eglInitialize:320>: EGL 1.4 QUALCOMM Build: I0404c4692afb8623f95c43aeb6d5e13ed4b30ddbDate: 11/06/13 
11-05 12:17:13.414 3683-3683/com.foo.ble D/OpenGLRenderer﹕ Enabling debug mode 0 
11-05 12:17:13.454 3683-3683/com.foo.ble D/dalvikvm﹕ GC_FOR_ALLOC freed 238K, 2% free 16971K/17240K, paused 14ms, total 14ms 
11-05 12:17:15.174 3683-3694/com.foo.ble D/foo﹕ onLeScan payload:02010517FF0000000000000005000000000000000000395401001207086C6976656C7911072E9AB04DB9790F857A4CF0634C7AB94A000000000000000000 
11-05 12:17:15.174 3683-3694/com.foo.ble D/BluetoothAdapter﹕ stopLeScan() 
11-05 12:17:15.184 3683-3683/com.foo.ble D/BluetoothGatt﹕ connect() - device: CE:AD:09:F2:BB:DC, auto: true 
11-05 12:17:15.184 3683-3683/com.foo.ble D/BluetoothGatt﹕ registerApp() 
11-05 12:17:15.184 3683-3683/com.foo.ble D/BluetoothGatt﹕ registerApp() - UUID=605cbd6f-6080-4fd8-97ff-1f97b406258e 
11-05 12:17:15.194 3683-3695/com.foo.ble D/BluetoothGatt﹕ onClientRegistered() - status=0 clientIf=5 
11-05 12:17:43.344 3683-3683/com.foo.ble D/BluetoothAdapter﹕ stopLeScan() 
11-05 12:23:59.194 3683-3694/com.foo.ble D/BluetoothGatt﹕ onClientConnectionState() - status=0 clientIf=5 device=CE:AD:09:F2:BB:DC 
11-05 12:23:59.194 3683-3694/com.foo.ble D/foo﹕ onConnectionStateChange 
11-05 12:23:59.204 3683-3694/com.foo.ble D/foo﹕ status connected 
11-05 12:23:59.204 3683-3694/com.foo.ble D/BluetoothGatt﹕ discoverServices() - device: CE:AD:09:F2:BB:DC 
11-05 12:24:00.754 3683-3695/com.foo.ble D/BluetoothGatt﹕ onGetService() - Device=CE:AD:09:F2:BB:DC UUID=randomService1 
11-05 12:24:00.764 3683-3694/com.foo.ble D/BluetoothGatt﹕ onGetService() - Device=CE:AD:09:F2:BB:DC UUID=randomService2 
11-05 12:24:00.784 3683-3695/com.foo.ble D/BluetoothGatt﹕ onGetService() - Device=CE:AD:09:F2:BB:DC UUID=something1 
11-05 12:24:00.794 3683-3694/com.foo.ble D/BluetoothGatt﹕ onGetCharacteristic() - Device=CE:AD:09:F2:BB:DC UUID=randomUUID1 
11-05 12:24:00.804 3683-3695/com.foo.ble D/BluetoothGatt﹕ onGetCharacteristic() - Device=CE:AD:09:F2:BB:DC UUID=randomUUID2 
11-05 12:24:00.804 3683-3694/com.foo.ble D/BluetoothGatt﹕ onGetCharacteristic() - Device=CE:AD:09:F2:BB:DC UUID=randomUUID3 
11-05 12:24:00.814 3683-3695/com.foo.ble D/BluetoothGatt﹕ onGetCharacteristic() - Device=CE:AD:09:F2:BB:DC UUID=randomUUID4 
11-05 12:24:00.814 3683-3694/com.foo.ble D/BluetoothGatt﹕ onGetCharacteristic() - Device=CE:AD:09:F2:BB:DC UUID=randomUUID5 
11-05 12:24:00.814 3683-3695/com.foo.ble D/BluetoothGatt﹕ onGetCharacteristic() - Device=CE:AD:09:F2:BB:DC UUID=randomUUID6 
11-05 12:24:00.814 3683-3694/com.foo.ble D/BluetoothGatt﹕ onGetCharacteristic() - Device=CE:AD:09:F2:BB:DC UUID=randomUUID7 
11-05 12:24:00.814 3683-3695/com.foo.ble D/BluetoothGatt﹕ onGetCharacteristic() - Device=CE:AD:09:F2:BB:DC UUID=something2 
11-05 12:24:00.824 3683-3694/com.foo.ble D/BluetoothGatt﹕ onGetDescriptor() - Device=CE:AD:09:F2:BB:DC UUID=something3 
11-05 12:24:00.824 3683-3695/com.foo.ble D/BluetoothGatt﹕ onSearchComplete() = Device=CE:AD:09:F2:BB:DC Status=0 
11-05 12:24:00.824 3683-3695/com.foo.ble D/foo﹕ onServicesDiscovered 
11-05 12:24:00.824 3683-3695/com.foo.ble D/BluetoothGatt﹕ setCharacteristicNotification() - uuid: something2 enable: true 
11-05 12:24:00.834 3683-3695/com.foo.ble D/BluetoothGatt﹕ writeDescriptor() - uuid: something3 
11-05 12:24:00.894 3683-3694/com.foo.ble D/BluetoothGatt﹕ onDescriptorWrite() - Device=CE:AD:09:F2:BB:DC UUID=something3 
11-05 12:24:00.894 3683-3694/com.foo.ble D/foo﹕ onDescriptorWrite status: 0 
11-05 12:24:00.894 3683-3694/com.foo.ble D/BluetoothGatt﹕ readCharacteristic() - uuid: something2 
11-05 12:24:00.994 3683-3695/com.foo.ble D/BluetoothGatt﹕ onCharacteristicRead() - Device=CE:AD:09:F2:BB:DC UUID=something2 Status=0 
11-05 12:24:00.994 3683-3695/com.foo.ble D/foo﹕ onCharacteristicRead status:0 payload: 0000000000050000000000000000005154010012 

任何幫助將不勝感激!

+0

什麼不工作?您的日誌似乎顯示成功狀態。 – 2014-11-05 21:48:20

+0

我沒有收到通知,沒有「onCharacteristicChanged」 – eipipuz 2014-11-05 21:53:28

+0

在調試過程中,如果您在OnCharacteristicChanged方法的其他地方設置了斷點,實際上您會收到來自遠程設備的通知。 – 2014-11-06 11:50:30

回答

2

我觀察到通知或者不能使用某些電話/外設/環境組合,或者在工作於相同連接會話後停止工作,沒有明顯的原因。所有通話都會返回成功,但通知不會通過。無論您是否解決眼前的問題,您都應該考慮爲您的特徵值進行讀取輪詢,作爲備用解決方案。

您的onServicesDiscovered()方法中的各種調用也應該通過對您調用的各種gatt方法的返回值進行條件檢查來進行門控。

無論如何,有些事情你可以嘗試:

  • 嘗試重置藍牙堆棧通過你的手機設置,甚至重新啓動整個手機。
  • 測試其他設備。有適用於iOS的應用程序可讓您的iPhone充當外圍設備。嘗試連接到這樣的「模擬設備」,看看通知是否在這種情況下工作。
  • 嘗試其他Android手機。我觀察到,如果這就是您使用的Nexus 4,它的通知最多。

最後,我會像技術支持一樣詢問計算機是否插入:確保外設的特性值確實在變化。

+0

感謝您的回答Doug 。我不知道如何繼續。正如你所說,一旦我嘗試使用另一部電話,相同的代碼工作。對於nexus,我發現這是一個等待高達7分鐘的問題o對於S4我一直無法使它工作(是的,我正在運行在UI線程上)。我不知道什麼是解決這個問題的最好方法。 – eipipuz 2014-11-14 17:34:58

+0

7分鐘等待通過將device.connectGatt(MainActivity.this,true,callback);更改爲'device.connectGatt(MainActivity.this,false,callback);'來「固定」。我不知道第二個參數造成如此大的差異。 – eipipuz 2014-11-14 17:37:52

+0

是autoConnect非常複雜。對於某些電話和無線設備的組合,這個布爾值必須是真的,對於其他的則是假的。對於其他人來說,真實的是更持續成功,但連接起來更慢對於強大的連接流程,您必須在運行時進行基本實驗,即時嘗試不同的組合,直至成功連接,然後記住後續連接嘗試的設置。如果最終使用「autoConnect」設置爲true,則必須小心,因爲本機堆棧可以非常積極地嘗試永久連接,甚至是首先被發現。 – 2014-11-14 20:36:16