Android蓝牙低功耗读取GATT属性的问题



我正试图从GATT服务器读取值并在我的屏幕上显示它。我已经通过各种教程,并试图找到各种方法来解决这个问题,但我绊倒的地方!

如果你能帮助我,我会很感激的。

布局XML:

<?xml version="1.0" encoding="utf-8"?>

<TextView android:text="Key : " android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/textView2" />
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:text="Medium Text"
    android:id="@+id/pubkey"
    android:layout_alignTop="@+id/textView2"
    android:layout_toEndOf="@+id/textView2"
    android:layout_marginStart="30dp" />
</RelativeLayout>

MainActivity.java

public class MainActivity extends Activity implements  BluetoothAdapter.LeScanCallback {
private static final String TAG = "BluetoothGattActivity";
private static final String DEVICE_NAME = "PUNEu0005u0012b";
/*
SECURITY SERVICE
 */
private static final UUID SECURITY_SERVICE = UUID.fromString("3E099914-293F-11E4-93BD-AFD0FE6D1DFD");
private static final UUID SECURITY_PUBLICKEY = UUID.fromString("3E099915-293F-11E4-93BD-AFD0FE6D1DFD");

private BluetoothAdapter mBluetoothAdapter;
private SparseArray<BluetoothDevice> mDevices;
private BluetoothGatt mConnectedGatt;
private  TextView publicKEY;
private ProgressDialog mProgress;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
    setContentView(R.layout.activity_main);
    setProgressBarIndeterminate(true);
    publicKEY = (TextView) findViewById(R.id.pubkey);

    BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
    mBluetoothAdapter = manager.getAdapter();
    mDevices = new SparseArray<BluetoothDevice>();
    mProgress = new ProgressDialog(this);
    mProgress.setIndeterminate(true);
    mProgress.setCancelable(false);
}
@Override
protected void onResume() {
    super.onResume();
    /*
     * We need to enforce that Bluetooth is first enabled, and take the
     * user to settings to enable it if they have not done so.
     */
    if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
        //Bluetooth is disabled
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivity(enableBtIntent);
        finish();
        return;
    }
    /*
     * Check for Bluetooth LE Support.  In production, our manifest entry will keep this
     * from installing on these devices, but this will allow test devices or other
     * sideloads to report whether or not the feature exists.
     */
    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
        Toast.makeText(this, "No LE Support.", Toast.LENGTH_SHORT).show();
        finish();
        return;
    }
    clearDisplayValues();
}

@Override
protected void onPause() {
    super.onPause();
    //Make sure dialog is hidden
    mProgress.dismiss();
    //Cancel any scans in progress
    mHandler.removeCallbacks(mStopRunnable);
    mHandler.removeCallbacks(mStartRunnable);
    mBluetoothAdapter.stopLeScan(this);
}
@Override
protected void onStop() {
    super.onStop();
    //Disconnect from any active tag connection
    if (mConnectedGatt != null) {
        mConnectedGatt.disconnect();
        mConnectedGatt = null;
    }
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Add the "scan" option to the menu
    getMenuInflater().inflate(R.menu.main, menu);
    //Add any device elements we've discovered to the overflow menu
    for (int i=0; i < mDevices.size(); i++) {
        BluetoothDevice device = mDevices.valueAt(i);
        menu.add(0, mDevices.keyAt(i), 0, device.getName());
    }
    return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.action_scan:
            mDevices.clear();
            startScan();
            return true;
        default:
            //Obtain the discovered device to connect with
            BluetoothDevice device = mDevices.get(item.getItemId());
            Log.i(TAG, "Connecting to "+device.getName());
            /*
             * Make a connection with the device using the special LE-specific
             * connectGatt() method, passing in a callback for GATT events
             */
            mConnectedGatt = device.connectGatt(this, false, mGattCallback);
            //Display progress UI
            mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "Connecting to "+device.getName()+"..."));
            return super.onOptionsItemSelected(item);
    }
}
private void clearDisplayValues() {
    publicKEY.setText("---");
}
private Runnable mStopRunnable = new Runnable() {
    @Override
    public void run() {
        stopScan();
    }
};
private Runnable mStartRunnable = new Runnable() {
    @Override
    public void run() {
        startScan();
    }
};
private void startScan() {
    mBluetoothAdapter.startLeScan(this);
    setProgressBarIndeterminateVisibility(true);
    mHandler.postDelayed(mStopRunnable, 2500);
}
private void stopScan() {
    mBluetoothAdapter.stopLeScan(this);
    setProgressBarIndeterminateVisibility(false);
}

和我所有的回调都在这里

/* BluetoothAdapter.LeScanCallback */

@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
    Log.i(TAG, "New LE Device: " + device.getName() + " @ " + rssi);
    /*
     * We are looking for SensorTag devices only, so validate the name
     * that each device reports before adding it to our collection
     */
    if (DEVICE_NAME.equals(device.getName())) {
        mDevices.put(device.hashCode(), device);
        //Update the overflow menu
        invalidateOptionsMenu();
    }
}
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

    private int mState = 0;
    private void reset()
    {
     mState= 0;
    }
    private void advance()
    {
        mState++;
    }
    private void enabled(BluetoothGatt gatt) {
        BluetoothGattCharacteristic characteristic;
        switch(mState) {
            case 0:
                Log.d(TAG, "Enable Public Key");
                characteristic = gatt.getService(SECURITY_SERVICE).getCharacteristic(SECURITY_PUBLICKEY);
                break;
            default:
                mHandler.sendEmptyMessage(MSG_DISMISS);
                Log.i(TAG, "All Sensors Enabled");
                return;
        }

    }
    private void readData(BluetoothGatt gatt)
    {
        BluetoothGattCharacteristic characteristic;
        switch (mState) {
            case 0:
                Log.d(TAG, "Reading Public Key");
                characteristic = gatt.getService(SECURITY_SERVICE).getCharacteristic(SECURITY_PUBLICKEY);
                break;
            default:
                mHandler.sendEmptyMessage(MSG_DISMISS);
                Log.i(TAG, "All Sensors Enabled");
                return;
        }
        gatt.readCharacteristic(characteristic);

    }
    private void enableNotif(BluetoothGatt gatt) {
        BluetoothGattCharacteristic characteristic;
        switch (mState) {
            case 0:
                Log.d(TAG, "Setting Notification Public Key");
                characteristic = gatt.getService(SECURITY_SERVICE).getCharacteristic(SECURITY_PUBLICKEY);
                break;
            default:
                mHandler.sendEmptyMessage(MSG_DISMISS);
                Log.i(TAG, "All Sensors Enabled");
                return;
        }
        gatt.setCharacteristicNotification(characteristic, true);
    }
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        Log.d(TAG, "Connection State Change: " + status + " -> " + connectionState(newState));
        if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {
            /*
             * Once successfully connected, we must next discover all the services on the
             * device before we can read and write their characteristics.
             */
            gatt.discoverServices();
            mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "Discovering Services..."));
        } else if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_DISCONNECTED) {
            /*
             * If at any point we disconnect, send a message to clear the weather values
             * out of the UI
             */
            mHandler.sendEmptyMessage(MSG_CLEAR);
        } else if (status != BluetoothGatt.GATT_SUCCESS) {
            /*
             * If there is a failure at any stage, simply disconnect
             */
            gatt.disconnect();
        }
    }
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        Log.d(TAG, "Services Discovered: " + status);
        mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "Enabling Public Key..."));
        reset();
        enabled(gatt);
    }
    @Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,int status ) {
        if(SECURITY_PUBLICKEY.equals(characteristic.getUuid())) {
            mHandler.sendMessage(Message.obtain(null,MSG_PUBLICKEY,characteristic));
        }
        enableNotif(gatt);
        readData(gatt);
    }

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        //After writing the enable flag, next we read the initial value
        readData(gatt);
    }
    @Override
    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
        Log.d(TAG, "Remote RSSI: " + rssi);
    }
    private String connectionState(int status) {
        switch (status) {
            case BluetoothProfile.STATE_CONNECTED:
                return "Connected";
            case BluetoothProfile.STATE_DISCONNECTED:
                return "Disconnected";
            case BluetoothProfile.STATE_CONNECTING:
                return "Connecting";
            case BluetoothProfile.STATE_DISCONNECTING:
                return "Disconnecting";
            default:
                return String.valueOf(status);
        }
    }
};

private static final int MSG_PROGRESS = 201;
private static final int MSG_DISMISS = 202;
private static final int MSG_CLEAR = 301;
private static final int MSG_PUBLICKEY = 101;
private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        BluetoothGattCharacteristic characteristic;
        switch (msg.what) {
            case MSG_PUBLICKEY:
                characteristic = (BluetoothGattCharacteristic) msg.obj;
                if (characteristic.getValue() == null) {
                    Log.w(TAG, "Error obtaining humidity value");
                    return;
                }
                updatePubKey(characteristic);
                break;
            case MSG_PROGRESS:
                mProgress.setMessage((String) msg.obj);
                if (!mProgress.isShowing()) {
                    mProgress.show();
                }
                break;
            case MSG_DISMISS:
                mProgress.hide();
                break;
            case MSG_CLEAR:
                clearDisplayValues();
                break;
        }
    }
};
private void updatePubKey(BluetoothGattCharacteristic characteristic) {
    byte[] data = characteristic.getValue();
    final StringBuilder stringBuilder = new StringBuilder(data.length);
    for(byte byteChar : data)
        stringBuilder.append(String.format("%02X ", byteChar));
    publicKEY.setText(new String(data)+" "+stringBuilder.toString());
}

}

这是日志

Timeline: Timeline: Activity_idle id: android.os.BinderProxy@41cc5460         time:10882957
BluetoothAdapter: startLeScan(): null
BluetoothAdapter: onClientRegistered() - status=0 clientIf=5
BluetoothGattActivity: New LE Device: null @ -83
BluetoothAdapter: stopLeScan()
BluetoothAdapter: startLeScan(): null
BluetoothAdapter: onClientRegistered() - status=0 clientIf=5
BluetoothGattActivity: New LE Device: PUNE @ -38
BluetoothAdapter: stopLeScan()
BluetoothGattActivity: Connecting to PUNE
BluetoothGatt: connect() - device: B0:B4:48:BA:40:84, auto: false
BluetoothGatt: registerApp()
BluetoothGatt: registerApp() - UUID=0bdf35a7-d4d0-4048-be15-a6cb030626f5
BluetoothGatt: onClientRegistered() - status=0 clientIf=5
BluetoothGatt: onClientConnectionState() - status=0 clientIf=5                device=B0:B4:48:BA:40:84
BluetoothGattActivity: Connection State Change: 0 -> Connected
BluetoothGatt: discoverServices() - device: B0:B4:48:BA:40:84

我正在寻找的服务在这里找到…

onGetCharacteristic() - Device=B0:B4:48:BA:40:84 UUID=3e099919-293f-11e4-93bd-afd0fe6d1dfd
onGetCharacteristic() - Device=B0:B4:48:BA:40:84 UUID=3e099915-293f-11e4-93bd-afd0fe6d1dfd
onGetCharacteristic() - Device=B0:B4:48:BA:40:84 UUID=3e099916-293f-11e4-93bd-afd0fe6d1dfd
onGetCharacteristic() - Device=B0:B4:48:BA:40:84 UUID=3e099917-293f-11e4-93bd-afd0fe6d1dfd

问题就出在这里了。

 D/BluetoothGattActivity: Services Discovered: 0
 D/BluetoothGattActivity: Enable Public Key
 W/BluetoothGatt: Unhandled exception in callback
 W/BluetoothGatt: java.lang.NullPointerException
 W/BluetoothGatt:     at gune.blegune.MainActivity$3.enabled(MainActivity.java:232)
 W/BluetoothGatt:     at gune.blegune.MainActivity$3.onServicesDiscovered(MainActivity.java:314)
 W/BluetoothGatt:     at android.bluetooth.BluetoothGatt$1.onSearchComplete(BluetoothGatt.java:295)
 W/BluetoothGatt:     at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:215)
 W/BluetoothGatt:     at android.os.Binder.execTransact(Binder.java:404)
 W/BluetoothGatt:     at dalvik.system.NativeStart.run(Native Method)
 D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=5 device=B0:B4:48:BA:40:84
 D/BluetoothGattActivity: Connection State Change: 0 -> Disconnected
 D/BluetoothAdapter: stopLeScan()

。如果能提供帮助,我将不胜感激。很多谢谢!

我猜这是抛出NPE的行:

characteristic = gatt.getService(SECURITY_SERVICE).getCharacteristic(SECURITY_PUBLICKEY);

所以要么

gatt为null或

gatt.getService (SECURITY_SERVICE)

返回null。

你能添加一些日志记录来找出是哪个吗?

我的猜测是它的第二个在这种情况下,我建议你调用getServices()上的BluetoothGatt对象,并记录每个BluetoothGattService对象在结果列表中的UUID,以双重检查你有你认为你应该有服务UUID。

getService的规范说"如果支持BluetoothGattService,如果远程设备不提供请求的服务,则为null。"

最新更新