这是我第一次在Android项目中执行蓝牙低功耗。我正在做的项目基本上是检测所有蓝牙LE设备并连接它们以发现它们的服务。
我想问一下是否有人知道如何在ScanCallback中调用onScanResult(),onBatchScanResults()和onScanFailed()方法?
首先,运行scanLeDevice()方法。
BluetoothLeScanner mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
ScanSettings settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.build();
List<ScanFilter> filters = new ArrayList<ScanFilter>();
scanLeDevice(true);
在这种方法中,它将启动扫描。所以我假设扫描结果是使用这些回调传递的。
@TargetApi(21)
private void scanLeDevice(final boolean enable) {
if (enable) {
//stops scanning after a pre-defined scan period
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
System.out.println("BLE// mLEScanner.stopScan(mScanCallback) ");
mLEScanner.stopScan(mScanCallback);
}
}
}, SCAN_PERIOD);
System.out.println("BLE// mLEScanner.startScan(filters, settings, mScanCallback)");
mLEScanner.startScan(filters, settings, mScanCallback);
} else {
System.out.println("BLE// mLEScanner.stopScan(mScanCallback)");
mLEScanner.stopScan(mScanCallback);
}
}
但是,在ScanCallback中,我不知道它是如何触发onScanResult并使用回调传递扫描结果的。在我的测试中(如下所示),既不调用onScanResult()也不调用onBatchScanResults()和onScanFailed()。有人可以向我解释这个概念吗?这对我有很大的帮助!
/* Scan result for SDK >= 21 */
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
System.out.println("BLE// onScanResult");
Log.i("callbackType", String.valueOf(callbackType));
Log.i("result", result.toString());
BluetoothDevice btDevice = result.getDevice();
connectToDevice(btDevice);
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
System.out.println("BLE// onBatchScanResults");
for (ScanResult sr : results) {
Log.i("ScanResult - Results", sr.toString());
}
}
@Override
public void onScanFailed(int errorCode) {
System.out.println("BLE// onScanFailed");
Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
02-17 10:38:38.513 878-895/? D/BluetoothManagerService: Added callback: android.bluetooth.IBluetoothManagerCallback$Stub$Proxy@8334cf4:true
02-17 10:38:38.520 782-782/? D/BluetoothAdapter: STATE_ON
02-17 10:38:38.529 21554-21590/? D/BtGatt.GattService: registerClient() - UUID=835342c6-81eb-4e09-9729-5bbe1c22bc86
02-17 10:38:38.529 21554-21570/? D/BtGatt.GattService: onClientRegistered() - UUID=835342c6-81eb-4e09-9729-5bbe1c22bc86, clientIf=5
02-17 10:38:38.530 782-793/? D/BluetoothLeScanner: onClientRegistered() - status=0 clientIf=5
02-17 10:38:38.530 21554-21599/? D/BtGatt.GattService: start scan with filters
02-17 10:38:38.532 782-782/? I/System.out: BLE// mLEScanner.startScan(filters, settings, mScanCallback)
02-17 10:38:38.532 21554-21573/? D/BtGatt.ScanManager: handling starting scan
02-17 10:38:38.534 21576-21577/? I/WCNSS_FILTER: ibs_msm_serial_clock_vote: vote UART CLK ON using UART driver's ioctl()
02-17 10:38:38.542 21554-21570/? D/BtGatt.GattService: onScanFilterEnableDisabled() - clientIf=5, status=0, action=1
02-17 10:38:38.543 21554-21570/? D/BtGatt.ScanManager: callback done for clientIf - 5 status - 0
02-17 10:38:38.543 21554-21573/? D/BtGatt.ScanManager: configureFilterParamter 500 10000 1 0
02-17 10:38:38.547 21554-21570/? D/BtGatt.GattService: onScanFilterParamsConfigured() - clientIf=5, status=0, action=0, availableSpace=15
02-17 10:38:38.547 21554-21570/? D/BtGatt.ScanManager: callback done for clientIf - 5 status - 0
02-17 10:38:38.548 21554-21573/? D/BtGatt.ScanManager: configureRegularScanParams() - queue=1
02-17 10:38:38.548 487-2827/? I/ACDB-LOADER: ACDB AFE returned = -19
02-17 10:38:38.549 21554-21573/? D/BtGatt.ScanManager: configureRegularScanParams() - ScanSetting Scan mode=0 mLastConfiguredScanSetting=-2147483648
02-17 10:38:38.549 21554-21573/? D/BtGatt.ScanManager: configureRegularScanParams - scanInterval = 8000configureRegularScanParams - scanWindow = 800
02-17 10:38:38.549 21554-21570/? D/BtGatt.GattService: onScanParamSetupCompleted : 0
02-17 10:38:38.568 21554-21574/? W/bt_hci: filter_incoming_event command complete event with no matching command. opcode: 0x0.
02-17 10:38:38.603 21554-21570/? D/bt_btif_gattc: btif_gattc_update_properties BLE device name=Polar HR Sensor len=15 dev_type=2
02-17 10:38:39.571 21576-21585/? I/WCNSS_FILTER: ibs_msm_serial_clock_vote: vote UART CLK OFF using UART driver's ioctl()
02-17 10:38:43.526 782-782/? I/System.out: BLE// mLEScanner.stopScan(mScanCallback)
02-17 10:38:43.599 21576-21576/? I/WCNSS_FILTER: ibs_msm_serial_clock_vote: vote UART CLK ON using UART driver's ioctl()
02-17 10:38:43.967 21576-21576/? I/WCNSS_FILTER: ibs_msm_serial_clock_vote: vote UART CLK OFF using UART driver's ioctl()
使用带有API 23的Android手机
我在这里写的代码是:http://www.truiton.com/2015/04/android-bluetooth-low-energy-ble-example/
[更新的代码V1] - 无法正常工作
这是我的所有代码 我创建了一个虚拟外围设备,它处于广告模式。虚拟外围设备是通过名为LightBlue的应用程序创建的:https://itunes.apple.com/us/app/lightblue-explorer-bluetooth/id557428110?mt=8 请帮我查一下我的代码:)
@TargetApi(21)
public class BluetoothLE extends Fragment {
View view;
private BluetoothAdapter mBluetoothAdapter;
private int REQUEST_ENABLE_BT = 1;
private Handler mHandler;
private static final long SCAN_PERIOD = 5000; // Stops scanning after 5 seconds
private BluetoothLeScanner mLEScanner;
private BluetoothGatt mGatt; //To provide bluetooth communication
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;
private int permissionCheck;
public BluetoothLE(){
//empty constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.fragment_bluetooth, container, false);
mHandler = new Handler();
/* check if BLE is supported in this phone */
if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(getActivity(), "BLE Not Supported", Toast.LENGTH_SHORT).show();
getActivity().finish();
}
/* Enable bluetooth without leaving app */
final BluetoothManager bluetoothManager =
(BluetoothManager) getActivity().getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
/* Build ScanSetting */
ScanSettings.Builder scanSetting = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.setReportDelay(5000);
settings = scanSetting.build();
return view;
}
@TargetApi(Build.VERSION_CODES.M)
@Override
public void onResume() {
super.onResume();
/* Ensures Bluetooth is available on the device and it is enabled. If not, displays a dialog requesting user permission to enable Bluetooth. */
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { //Unable to obtain a BluetoothAdapter
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); //trigger onActivityResult
} else {
if (Build.VERSION.SDK_INT >= 21) {
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.build();
filters = new ArrayList<ScanFilter>();
}
if(Build.VERSION.SDK_INT >= 23){
checkLocationPermission();
}
scanLeDevice(true);
}
}
@Override
public void onPause() {
super.onPause();
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
scanLeDevice(false);
}
}
@Override
public void onDestroy() {
if (mGatt == null) {
return;
}
mGatt.close();
mGatt = null;
super.onDestroy();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
System.out.println("BLE// onActivityResult");
if (requestCode == REQUEST_ENABLE_BT) {
if (resultCode == Activity.RESULT_CANCELED) {
//Bluetooth not enabled.
getActivity().finish();
return;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
private void scanLeDevice(final boolean enable) {
if (enable) {
//stops scanning after a pre-defined scan period
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (Build.VERSION.SDK_INT < 21) {
System.out.println("BLE// mBluetoothAdapter.stopLeScan(mLeScanCallback) ");
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
System.out.println("BLE// mLEScanner.stopScan(mScanCallback) ");
}
}
}, SCAN_PERIOD);
if (Build.VERSION.SDK_INT < 21) {
System.out.println("BLE// mBluetoothAdapter.startLeScan(mLeScanCallback)");
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mLEScanner.startScan(mScanCallback);
//mLEScanner.startScan(filters, settings, mScanCallback);
System.out.println("BLE// mLEScanner.startScan(mScanCallback) ");
}
} else {
if (Build.VERSION.SDK_INT < 21) {
System.out.println("BLE// mBluetoothAdapter.stopLeScan(mLeScanCallback)");
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
System.out.println("BLE// mLEScanner.stopScan(mScanCallback)");
mLEScanner.stopScan(mScanCallback);
}
}
}
/* Scan result for SDK >= 21 */
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
System.out.println("BLE// onScanResult");
super.onScanResult(callbackType, result);
Log.i("callbackType", String.valueOf(callbackType));
Log.i("result", result.toString());
Log.i("Device Name: ", result.getDevice().getName());
System.out.println("Signal: " + result.getRssi());
BluetoothDevice btDevice = result.getDevice();
connectToDevice(btDevice);
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
System.out.println("BLE// onBatchScanResults");
for (ScanResult sr : results) {
Log.i("ScanResult - Results", sr.toString());
}
}
@Override
public void onScanFailed(int errorCode) {
System.out.println("BLE// onScanFailed");
Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
// scan results are returned here SDK < 21
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
System.out.println("BLE// DEVICDE FOUND");
Log.i("onLeScan", device.toString());
connectToDevice(device);
}
});
}
};
public void connectToDevice(BluetoothDevice device) {
System.out.println("BLE// connectToDevice()");
if (mGatt == null) {
mGatt = device.connectGatt(getActivity(), false, gattCallback); //Connect to a GATT Server
//scanLeDevice(false);// will stop after first device detection
}
}
private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
System.out.println("BLE// BluetoothGattCallback");
Log.i("onConnectionStateChange", "Status: " + status);
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
Log.i("gattCallback", "STATE_CONNECTED");
gatt.discoverServices();
break;
case BluetoothProfile.STATE_CONNECTING:
Log.i("gattCallback", "STATE_CONNECTING");
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.e("gattCallback", "STATE_DISCONNECTED");
break;
default:
Log.e("gattCallback", "STATE_OTHER");
}
}
@Override
//New services discovered
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
List<BluetoothGattService> services = gatt.getServices();
Log.i("onServicesDiscovered", services.toString());
gatt.readCharacteristic(services.get(1).getCharacteristics().get
(0));
}
@Override
//Result of a characteristic read operation
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic
characteristic, int status) {
Log.i("onCharacteristicRead", characteristic.toString());
gatt.disconnect();
}
};
public void checkLocationPermission(){
permissionCheck = ContextCompat.checkSelfPermission(getActivity(), android.Manifest.permission.ACCESS_COARSE_LOCATION);
switch(permissionCheck){
case PackageManager.PERMISSION_GRANTED:
break;
case PackageManager.PERMISSION_DENIED:
if(ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), android.Manifest.permission.ACCESS_COARSE_LOCATION)){
//Show an explanation to user *asynchronouselly* -- don't block
//this thread waiting for the user's response! After user sees the explanation, try again to request the permission
Snackbar.make(view, "Location access is required to show Bluetooth devices nearby.",
Snackbar.LENGTH_LONG).setAction("Action", null).show();
}
else{
//No explanation needed, we can request the permission
ActivityCompat.requestPermissions(getActivity(), new String[]{android.Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
}
break;
}
}
}
答案 0 :(得分:13)
所以..我终于找到了答案。
对于Android 6.0或更高版本的Android设备(例如我的手机是Nexus 5x),手机设置中的 GPS 和蓝牙都必须打开,另外在您的清单中必须添加BLUETOOTH
BLUETOOTH_ADMIN
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
权限。
现在一切正常对我来说:)
答案 1 :(得分:10)
我发现您使用的是public void startScan (List<ScanFilter> filters, ScanSettings settings, ScanCallback callback)
startScan()
方法,但您从未定义任何过滤器。而是传递一个空的ScanFilters ArrayList。因此,您无法获得任何回调,因为您没有为过滤器提供任何匹配标准。
由于您说要扫描所有 BLE设备,因此根本不需要使用任何过滤器。相反,请使用更简单的public void startScan (ScanCallback callback)
方法,该方法不使用任何过滤器或专门设置。
关于你理解它是如何工作的请求 - 我认为你根据你的代码以及你应该触发回调的期望来理解这个概念。您开始扫描,系统关闭并执行扫描而不会阻止您的代码执行(即它是异步执行)。在扫描发生时,它会在适当的时候调用回调对象中的三个方法之一(如API文档中所述)。这就是它。
<强>更新强>:
确保您请求BLUETOOTH,BLUETOOTH_ADMIN权限以及ACCESS_COARSE_LOCATION 或 ACCESS_FINE_LOCATION权限。这些是从startScan()
方法接收回调所必需的。不幸的是,如果您没有请求这些持久性,那么扫描就会无声地失败。我希望系统在日志中提供警告消息或触发对onScanFailed()
方法的回调,并使用错误代码指示问题。