2017-01-02 57 views
5

我有一個與我的Nexus 5X(運行Android 7.1)配對的藍牙耳機,並且我想連接到耳機的GATT服務器。我用下面的代碼試了一下:onServices在連接到GATT服務器時從未被調用

private BluetoothGattCallback btleGattCallback = new BluetoothGattCallback() { 
    @Override 
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { 
     Log.d(TAG, "onConnectionStateChange: " + status + ", " + newState); 

     if(newState == STATE_CONNECTED) { 
      Log.d(TAG, "Device connected"); 
      boolean ans = gatt.discoverServices(); 
      Log.d(TAG, "Discover Services started: " + ans); 
     } 
    } 

    @Override 
    public void onServicesDiscovered(BluetoothGatt gatt, int status) { 
      Log.d(TAG, "Number of Services: " + gatt.getServices().size()); 
    } 
}; 

public void onDeviceClicked(BluetoothDevice device) { 
    BluetoothGatt gatt = device.connectGatt(this, false, btleGattCallback); 
    Log.d(TAG, "Connected to GATT: " + gatt.connect()); 
} 

如果我點擊我的UI onDeviceClicked耳機打電話,談到這個日誌輸出:

<!-- language: lang-none --> 
Connected to GATT: true 
onConnectionStateChange: 0, 2 // GATT_SUCCESS, STATE_CONNECTED 
Device connected 
Discover Services started: true 

正如你可以看到onServicesDiscovered是從來沒有發射。我試圖撥打connectGattTRANSPORT_LEref),但後來我得到一個onConnectionStateChange: 133, 0。我還發現this question這就是爲什麼我加入了回答二中提到的gatt.connect()方法的原因。

你有什麼想法,爲什麼我沒有得到onServicesDiscovered回調?

+0

在你的代碼示例中,你引用'mBluetoothGatt.discoverServices();'和一個局部變量'BluetoothGatt gatt = device.connectGatt(this,false,btleGattCallback);'?它可能不是同一個實例或設備嗎? – ayvazj

+0

對不起,這是出於測試原因。我在上面的代碼中改變了它,但它也不起作用。 – Cilenco

回答

4

東西已經非常有用,我是等待大約600毫秒的連接建立後,然後啓動該服務發現。

+0

哇,工作!謝謝'onServicesDiscovered'現在被調用,但如果我調用'gatt.getServices()'它會返回一個空列表。任何想法爲什麼? – Cilenco

+0

@Cilenco您還必須通過檢查狀態== GATT_SUCCESS來驗證服務發現實際上是否成功。檢查此更多信息:https://developer.android.com/reference/android/bluetooth/BluetoothGattCallback.html#onServicesDiscovered(android.bluetooth.BluetoothGatt,int)。如果沒有,那麼在開始服務發現之前可能需要等待更長的時間。如果它仍然不起作用,那麼也許你的外圍設備有其他問題。 –

6

Android上的BLE可能會有點挑剔。

確保您在UI線程上調用mBluetoothGatt.discoverServices()。

if(newState == STATE_CONNECTED) { 
    Log.d(TAG, "Device connected"); 
    new Handler(Looper.getMainLooper()).post(new Runnable() { 
     @Override 
     public void run() { 
      boolean ans = mBluetoothGatt.discoverServices(); 
      Log.d(TAG, "Discover Services started: " + ans); 
     } 
    }); 
} 

也可以嘗試做BluetoothGatt gatt一個字段變量而不是局部變量。

如果您正在做任何重要的工作,請嘗試使用一個掩蓋所有特質的庫,以便您可以專注於高級邏輯。 https://github.com/Polidea/RxAndroidBle

以下是如何讀取特徵的示例。

 connectionObservable 
       .flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUuid)) 
       .observeOn(AndroidSchedulers.mainThread()) 
       .subscribe(bytes -> { 
        readOutputView.setText(new String(bytes)); 
        readHexOutputView.setText(HexString.bytesToHex(bytes)); 
        writeInput.setText(HexString.bytesToHex(bytes)); 
       }, this::onReadFailure); 

或者與Java 7的語法

 connectionObservable 
       .flatMap(new Func1<RxBleConnection, Observable<byte[]>>() { 
        @Override 
        public Observable<byte[]> call(RxBleConnection rxBleConnection) { 
         return rxBleConnection.readCharacteristic(characteristicUuid); 
        } 
       }) 
       .observeOn(AndroidSchedulers.mainThread()) 
       .subscribe(new Subscriber<byte[]>() { 
        @Override 
        public void onCompleted() { 

        } 

        @Override 
        public void onError(Throwable e) { 
         onReadFailure(e); 
        } 

        @Override 
        public void onNext(byte[] bytes) { 
         readOutputView.setText(new String(bytes)); 
         readHexOutputView.setText(HexString.bytesToHex(bytes)); 
         writeInput.setText(HexString.bytesToHex(bytes)); 
        } 
       }); 
+0

這也沒有幫助,但API看起來不錯:)你能舉個例子說明如何讀取電池電量嗎?我對新的lambda表達式以及在示例中引用的方法感到困惑... – Cilenco

+0

Lambda和方法引用需要一些習慣,但它節省了大量的鍋爐板,我用一個使用Java 7的示例更新了我的答案句法。 Lambda和方法參考需要一些習慣,但它節省了大量的鍋爐板。 – ayvazj

+0

謝謝你的代碼:)但現在我得到了'STARTED RxBleRadioOperationConnect',之後'onConnectionStateChange newState = 0 status = 133',一切都在UI線程中......對此有何想法?是否有可能將'TRANSPORT_BREDR'設置爲運輸方式? – Cilenco

0

這可能有助於

我將分兩個步驟說明:連接和發現服務

連接: 從mainthread連接,如果版本更大

設置的自動重新連接到假

大於或等於M,設置Tranposrt類型

其他直接用反思和妥善處理

Handler(applicationContext.mainLooper).post { 
        Log.d(TAG, " Post is called inside mainlooper") 
        mBluetoothGatt = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 
         Log.d(TAG, " Is Or Greater than M $mBluetoothDevice") 
         mBluetoothDevice!!.connectGatt(this, false, 
           onGhattListener, TRANSPORT_LE) 
        } else { 
         Log.d(TAG, " Less than M") 
         try { 
          Log.d(TAG, " Trying TRANPORT LE with reflection") 
          val m = mBluetoothDevice!!.javaClass.getDeclaredMethod("connectGatt", Context::class.java, Boolean::class.javaPrimitiveType, BluetoothGattCallback::class.java, Int::class.javaPrimitiveType) 
          m.isAccessible = true 
          val transport = mBluetoothDevice!!.javaClass.getDeclaredField("TRANSPORT_LE").getInt(null) 
          m.invoke(mBluetoothDevice, this, false, onGhattListener, transport) as BluetoothGatt 
         } catch (e: Exception) { 
          e.printStackTrace() 
          Log.d(TAG, " Catch to call normal connection") 
          mBluetoothDevice!!.connectGatt(this, false, 
            onGhattListener) 
         } 
        } 
        Log.d(TAG, "mBluetooth gatt is $mBluetoothGatt") 
        mBluetoothGatt?.let { 
         refreshDeviceCache(mBluetoothGatt!!) 
        } 
       } 

disover服務:在onGhattListener,如果設備是從主線程

override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, 
             newState: Int) { 
     Log.d(TAG, "onConnectionStateChange $gatt and status $status and newstate $newState") 
     when (newState) { 
      BluetoothGatt.STATE_CONNECTED -> { 
       Handler(Looper.getMainLooper()).post { 
        gatt.discoverServices() 
       } 
      } 
      BluetoothGatt.STATE_DISCONNECTED -> { 

      } 
      BluetoothGatt.STATE_CONNECTING -> { 

      } 
      BluetoothGatt.STATE_DISCONNECTING -> { 

      } 
     } 
    } 

連接消防discoverServices()這可能會解決你的問題

帶反射的呼叫刷新方法

fun refreshDeviceCache(gatt: BluetoothGatt): Boolean { 
    try { 
     val localMethod = gatt.javaClass.getMethod("refresh") 
     if (localMethod != null) { 
      return localMethod.invoke(gatt) as Boolean 
     } 
    } catch (localException: Exception) { 
     Log.e(TAG, "An exception occured while refreshing device") 
     localException.printStackTrace() 
    } 
    return false 
} 
相關問題