草庐IT

android - 蓝牙 Gatt 回调不适用于 Lollipop 的新 API

coder 2023-11-19 原文

我目前有一种方法可以写入 BLE 设备以发出蜂鸣声。我的蓝牙回调如下:

ReadCharacteristic rc = new ReadCharacteristic(context, ds.getMacAddress(), serviceUUID, UUID.fromString(myUUID), "") {
                @Override
                public void onRead() {
                    Log.w(TAG, "callDevice onRead");
                    try{Thread.sleep(1000);}catch(InterruptedException ex){}
                    WriteCharacteristic wc = new WriteCharacteristic(activity, context, getMacAddress(), serviceUUID, UUID.fromString(myUUID), ""){
                        @Override
                        public void onWrite(){
                            Log.w(TAG, "callDevice onWrite");
                        }
                        @Override
                        public void onError(){
                            Log.w(TAG, "callDevice onWrite-onError");
                        }
                    };

//                  Store data in writeBuffer
                    wc.writeCharacteristic(writeBuffer);
                }

                @Override
                public void onError(){
                    Log.w(TAG, "callDevice onRead-onError");
                }
            };

            rc.readCharacteristic();

我的 ReadCharacteristic 实现如下:

public class ReadCharacteristic extends BluetoothGattCallback {
    public ReadCharacteristic(Context context, String macAddress, UUID service, UUID characteristic, Object tag) {
        mMacAddress = macAddress;
        mService = service;
        mCharacteristic = characteristic;
        mTag = tag;
        mContext = context;
        this.activity =activity;
        final BluetoothManager bluetoothManager =
                (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();

    }

    final private static String TAG = "ReadCharacteristic";
    private Object mTag;
    private String mMacAddress;
    private UUID mService;
    private UUID mCharacteristic;
    private byte[] mValue;
    private Activity activity;
    private BluetoothAdapter mBluetoothAdapter;
    private Context mContext;

    private int retry = 5;


    public String getMacAddress() {
        return mMacAddress;
    }

    public UUID getService() {
        return mService;
    }

    public UUID getCharacteristic() {
        return mCharacteristic;
    }

    public byte[] getValue() { return mValue; }

    public void onRead() {
        Log.w(TAG, "onRead: " + getDataHex(getValue()));
    }

    public void onError() {
        Log.w(TAG, "onError");
    }

    public void readCharacteristic(){
        if (retry == 0)
        {
            onError();
            return;
        }
        retry--;



                final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(getMacAddress());
                if (device != null) {
                    Log.w(TAG, "Starting Read [" + getService() + "|" + getCharacteristic() + "]");
                    final ReadCharacteristic rc = ReadCharacteristic.this;
                    device.connectGatt(mContext, false, rc);
                }

    }

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        Log.w(TAG,"onConnectionStateChange [" + status + "|" + newState + "]");
        if ((newState == 2)&&(status ==0)) {
            gatt.discoverServices();
        }

        else{
            Log.w(TAG, "[" + status + "]");
         //   gatt.disconnect();
            gatt.close();
            try
            {
                Thread.sleep(2000);
            }
            catch(Exception e)
            {

            }
            readCharacteristic();
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        Log.w(TAG,"onServicesDiscovered [" + status + "]");
        BluetoothGattService bgs = gatt.getService(getService());
        if (bgs != null) {
            BluetoothGattCharacteristic bgc = bgs.getCharacteristic(getCharacteristic());
            gatt.readCharacteristic(bgc);
        }
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt,
                                     BluetoothGattCharacteristic characteristic,
                                     int status) {
        Log.w(TAG,"onCharacteristicRead [" + status + "]");
        if (status == BluetoothGatt.GATT_SUCCESS) {
            mValue = characteristic.getValue();
            Log.w(TAG,"onCharacteristicRead [" + mValue + "]");
            gatt.disconnect();
            gatt.close();
            onRead();
        }

        else {
            gatt.disconnect();
            gatt.close();
        }
    }


}

当前的方法非常适合运行 KitKat 及以下版本的设备。但是,当我在 Lollipop 上运行相同的功能时,它会发出几次哔哔声,然后停止工作。从那时起,每当我尝试连接时,它都会说设备已断开连接,并在 OnConnectionStateChanged 方法中给我一个错误代码 257。

每当我调用这个方法时我也会得到这个错误 -

04-20 14:14:23.503  12329-12384/com.webble.xy W/BluetoothGatt﹕ Unhandled exception in callback
    java.lang.NullPointerException: Attempt to invoke virtual method 'void android.bluetooth.BluetoothGattCallback.onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)' on a null object reference
            at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:181)
            at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:70)
            at android.os.Binder.execTransact(Binder.java:446)

有遇到同样问题的吗?尝试调试时,我从未遇到过对象为 null 的情况。

最佳答案

问题已报告给谷歌 Issue 183108: NullPointerException in BluetoothGatt.java when disconnecting and closing .

解决方法是在您想要关闭 BLE 连接时调用 disconnect() - 然后仅在 onConnectionStateChange 中调用 close()回调:

  public void shutdown() {
    try {
      mBluetoothGatt.disconnect();
    } catch (Exception e) {
      Log.d(TAG, "disconnect ignoring: " + e);
    }
  }

  private final BluetoothGattCallback mGattCallback = 
   new BluetoothGattCallback() {
    @Override
      public void onConnectionStateChange(BluetoothGatt gatt, 
       int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {

        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {

          try {
            gatt.close();
          } catch (Exception e) {
            Log.d(TAG, "close ignoring: " + e);
          }
        }
      }

这是我的完整源代码(一个执行正常扫描、定向扫描和发现服务的类):

public class BleObject {

  public static final String ACTION_BLUETOOTH_ENABLED   = "action.bluetooth.enabled";
  public static final String ACTION_BLUETOOTH_DISABLED  = "action.bluetooth.disabled";
  public static final String ACTION_DEVICE_FOUND        = "action.device.found";
  public static final String ACTION_DEVICE_BONDED       = "action.device.bonded";
  public static final String ACTION_DEVICE_CONNECTED    = "action.device.connected";
  public static final String ACTION_DEVICE_DISCONNECTED = "action.device.disconnected";
  public static final String ACTION_POSITION_READ       = "action.position.read";

  public static final String EXTRA_BLUETOOTH_DEVICE     = "extra.bluetooth.device";
  public static final String EXTRA_BLUETOOTH_RSSI       = "extra.bluetooth.rssi";

  private Context mContext;
  private IntentFilter mIntentFilter;
  private LocalBroadcastManager mBroadcastManager;
  private BluetoothAdapter mBluetoothAdapter;
  private BluetoothGatt mBluetoothGatt;
  private BluetoothLeScanner mScanner;
  private ScanSettings mSettings;
  private List<ScanFilter> mScanFilters;

  private Handler mConnectHandler;

  public BleObject(Context context) {
    mContext = context;

    if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
      Log.d(TAG, "BLE not supported");
      return;
    }

    BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = bluetoothManager.getAdapter();
    if (mBluetoothAdapter == null) {
      Log.d(TAG, "BLE not accessible");
      return;
    }

    mIntentFilter = new IntentFilter();
    mIntentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
    mIntentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);

    mSettings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
    mScanFilters = new ArrayList<ScanFilter>();

    mConnectHandler = new Handler();

    mBroadcastManager = LocalBroadcastManager.getInstance(context);
  }

  public boolean isEnabled() {
    return (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled());
  }

  private ScanCallback mScanCallback = new ScanCallback() {
    @Override
      public void onScanResult(int callbackType, ScanResult result) {
        processResult(result);
      }

    @Override
      public void onBatchScanResults(List<ScanResult> results) {
        for (ScanResult result: results) {
          processResult(result);
        }
      }

    private void processResult(ScanResult result) {
      if (result == null)
        return;

      BluetoothDevice device = result.getDevice();
      if (device == null)
        return;

      Intent i = new Intent(Utils.ACTION_DEVICE_FOUND);
      i.putExtra(Utils.EXTRA_BLUETOOTH_DEVICE, device);
      i.putExtra(Utils.EXTRA_BLUETOOTH_RSSI, result.getRssi());
      mBroadcastManager.sendBroadcast(i);
    }
  };

  private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
    @Override
      public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
          if (gatt == null)
            return;

          BluetoothDevice device = gatt.getDevice();
          if (device == null)
            return;

          Log.d(TAG, "BluetoothProfile.STATE_CONNECTED: " + device);
          Intent i = new Intent(Utils.ACTION_DEVICE_CONNECTED);
          i.putExtra(Utils.EXTRA_BLUETOOTH_DEVICE, device);
          mBroadcastManager.sendBroadcast(i);

        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {

          Log.d(TAG, "BluetoothProfile.STATE_DISCONNECTED");
          Intent i = new Intent(Utils.ACTION_DEVICE_DISCONNECTED);
          mBroadcastManager.sendBroadcast(i);

          // Issue 183108: https://code.google.com/p/android/issues/detail?id=183108
          try {
            gatt.close();
          } catch (Exception e) {
            Log.d(TAG, "close ignoring: " + e);
          }
        }
      }

    @Override
      public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (gatt == null)
          return;

        for (BluetoothGattService service: gatt.getServices()) {
          Log.d(TAG, "service: " + service.getUuid());

          for (BluetoothGattCharacteristic chr: service.getCharacteristics()) {
            Log.d(TAG, "char: " + chr.getUuid());
          }
        }
      }
  };

  private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
      final String action = intent.getAction();

      if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
        final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);

        switch (state) {
          case BluetoothAdapter.STATE_TURNING_OFF: {
                                                     Log.d(TAG, "BluetoothAdapter.STATE_TURNING_OFF");
                                                     break;
                                                   }
          case BluetoothAdapter.STATE_OFF: {
                                             Log.d(TAG, "BluetoothAdapter.STATE_OFF");
                                             Intent i = new Intent(Utils.ACTION_BLUETOOTH_DISABLED);
                                             mBroadcastManager.sendBroadcast(i);
                                             break;
                                           }
          case BluetoothAdapter.STATE_TURNING_ON: {
                                                    Log.d(TAG, "BluetoothAdapter.STATE_TURNING_ON");
                                                    break;
                                                  }
          case BluetoothAdapter.STATE_ON: {
                                            Log.d(TAG, "BluetoothAdapter.STATE_ON");
                                            Intent i = new Intent(Utils.ACTION_BLUETOOTH_ENABLED);
                                            mBroadcastManager.sendBroadcast(i);
                                            break;
                                          }
        }
      } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
        final int state     = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);
        final int prevState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR);

        if (state == BluetoothDevice.BOND_BONDED &&
            prevState == BluetoothDevice.BOND_BONDING) {

          if (mBluetoothGatt != null) {
            BluetoothDevice device = mBluetoothGatt.getDevice();
            if (device == null)
              return;

            Intent i = new Intent(Utils.ACTION_DEVICE_BONDED);
            i.putExtra(Utils.EXTRA_BLUETOOTH_DEVICE, device);
            mBroadcastManager.sendBroadcast(i);
          }
        }
      }
    }
  };

  // scan for all BLE devices nearby
  public void startScanning() {
    Log.d(TAG, "startScanning");

    mScanFilters.clear();
    // create the scanner here, rather than in init() -
    // because otherwise app crashes when Bluetooth is switched on
    mScanner = mBluetoothAdapter.getBluetoothLeScanner();
    mScanner.startScan(mScanFilters, mSettings, mScanCallback);
  }

  // scan for a certain BLE device and after delay
  public void startScanning(final String address) {
    Log.d(TAG, "startScanning for " + address);

    mScanFilters.clear();
    mScanFilters.add(new ScanFilter.Builder().setDeviceAddress(address).build());
    // create the scanner here, rather than in init() -
    // because otherwise app crashes when Bluetooth is switched on
    mScanner = mBluetoothAdapter.getBluetoothLeScanner();
    mScanner.startScan(mScanFilters, mSettings, mScanCallback);
  }

  public void stopScanning() {
    Log.d(TAG, "stopScanning");

    if (mScanner != null) {
      mScanner.stopScan(mScanCallback);
      mScanner = null;
    }

    mScanFilters.clear();
  }

  public void connect(final BluetoothDevice device) {
    Log.d(TAG, "connect: " + device.getAddress() + ", mBluetoothGatt: " + mBluetoothGatt);

    mConnectHandler.post(new Runnable() {
        @Override
        public void run() {
        setPin(device, Utils.PIN);
        mBluetoothGatt = device.connectGatt(mContext, true, mGattCallback);
        }
        });
  }

  private void setPin(BluetoothDevice device, String pin) {
    if (device == null || pin == null || pin.length() < 4)
      return;

    try {
      device.setPin(pin.getBytes("UTF8"));
    } catch (Exception e) {
      Utils.logw("setPin ignoring: " + e);
    }
  }

  // called on successful device connection and will toggle reading coordinates
  public void discoverServices() {
    if (mBluetoothGatt != null)
      mBluetoothGatt.discoverServices();
  }

  public boolean isBonded(BluetoothDevice device) {
    Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
    if (bondedDevices == null || bondedDevices.size() == 0)
      return false;

    for (BluetoothDevice bondedDevice: bondedDevices) {
      Log.d(TAG, "isBonded bondedDevice: " + bondedDevice);

      if (bondedDevice.equals(device)) {
        Log.d(TAG, "Found bonded device: " + device);
        return true;
      }
    }

    return false;
  }

  public void startup() {
    try {
      mContext.registerReceiver(mReceiver, mIntentFilter);
    } catch (Exception e) {
      Log.d(TAG, "registerReceiver ignoring: " + e);
    }
  }

  public void shutdown() {
    Log.d(TAG, "BleObject shutdown");

    try {
      mContext.unregisterReceiver(mReceiver);
    } catch (Exception e) {
      Log.d(TAG, "unregisterReceiver ignoring: " + e);
    }

    try {
      stopScanning();
    } catch (Exception e) {
      Log.d(TAG, "stopScanning ignoring: " + e);
    }

    try {
      mBluetoothGatt.disconnect();
    } catch (Exception e) {
      Log.d(TAG, "disconnect ignoring: " + e);
    }

    mConnectHandler.removeCallbacksAndMessages(null);
  }
}

在每个 BLE 事件中,它通过 LocalBroadcastManager 广播 Intent 。

关于android - 蓝牙 Gatt 回调不适用于 Lollipop 的新 API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29758890/

有关android - 蓝牙 Gatt 回调不适用于 Lollipop 的新 API的更多相关文章

  1. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  2. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

  3. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  4. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

    我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

  5. ruby-on-rails - ActionController::RoutingError: 未初始化常量 Api::V1::ApiController - 2

    我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc

  6. ruby - inverse_of 是否适用于 has_many? - 2

    当我使用has_one时,它​​工作得很好,但在has_many上却不行。在这里您可以看到object_id不同,因为它运行了另一个SQL来再次获取它。ruby-1.9.2-p290:001>e=Employee.create(name:'rafael',active:false)ruby-1.9.2-p290:002>b=Badge.create(number:1,employee:e)ruby-1.9.2-p290:003>a=Address.create(street:"123MarketSt",city:"SanDiego",employee:e)ruby-1.9.2-p290

  7. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  8. ruby-on-rails - Mandrill API 模板 - 2

    我正在使用Mandrill的RubyAPIGem并使用以下简单的测试模板:testastic按照Heroku指南中的示例,我有以下Ruby代码:require'mandrill'm=Mandrill::API.newrendered=m.templates.render'test-template',[{:header=>'someheadertext',:main_section=>'Themaincontentblock',:footer=>'asdf'}]mail(:to=>"JaysonLane",:subject=>"TestEmail")do|format|format.h

  9. ruby-on-rails - 在 Ruby (on Rails) 中使用 imgur API 获取图像 - 2

    我正在尝试使用Ruby2.0.0和Rails4.0.0提供的API从imgur中提取图像。我已尝试按照Ruby2.0.0文档中列出的各种方式构建http请求,但均无济于事。代码如下:require'net/http'require'net/https'defimgurheaders={"Authorization"=>"Client-ID"+my_client_id}path="/3/gallery/image/#{img_id}.json"uri=URI("https://api.imgur.com"+path)request,data=Net::HTTP::Get.new(path

  10. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

随机推荐