我正在创建一个使用 Android Studio 扫描附近所有 BLE(低功耗蓝牙)设备的 Android 应用。
我想我已经实现了所有这些文档:
但正如您在下面的日志中看到的那样,(问题)应用没有扫描 BLE 设备。
这里是 logcat 中的日志:
我认为这可能是因为未调用 BLE scan callback
(日志中没有 BLE 扫描回调)。
所以我的问题是我的代码有什么解决方案?我是否错过了编码?
这是我的代码
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Example">
<activity android:name=".ui.authentication.signin.SignInActivity" />
<activity android:name=".ui.authentication.signup.SignUpActivity" />
<activity android:name=".ui.home.HomeActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
BluetoothHelper.java:
package com.example.util;
import android.Manifest;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
public class BluetoothHelper {
static int REQUEST_ENABLE_BT = 1;
static int PERMISSION_CODE = 1;
static String TAG = "Bluetooth";
static boolean isScanning = false;
// SOURCE FOR BLUETOOTH PERMISSION: https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#java
// CHECK FOR BLUETOOTH PERMISSION FOR ANDROID 10
public static void android10BluetoothPermission (Context context, Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (ContextCompat.checkSelfPermission(context,
Manifest.permission.ACCESS_BACKGROUND_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
activity,
new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
PERMISSION_CODE
);
}
}
}
// SOURCE FOR SET UP BLUETOOTH: https://developer.android.com/guide/topics/connectivity/bluetooth/setup#java
// GET BLUETOOTH ADAPTER
public static void setupBluetooth (BluetoothAdapter bluetoothAdapter) {
if (bluetoothAdapter == null) {
Log.d(TAG, "Bluetooth: " + "bluetooth adapter is null");
}
else {
Log.d(TAG, "Bluetooth: " + "bluetooth is adapter not null");
}
}
// ENABLE BLUETOOTH
public static void enableBluetooth (Activity activity, BluetoothAdapter bluetoothAdapter) {
Log.d(TAG, "Bluetooth: " + "bluetooth adapter is enabled: " + bluetoothAdapter.isEnabled());
if (!bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
Log.d(TAG, "Bluetooth: " + "bluetooth is enabled");
}
else {
Log.d(TAG, "Bluetooth: " + "bluetooth is already enabled");
}
}
// SOURCE FOR FIND BLUETOOTH DEVICES: https://developer.android.com/guide/topics/connectivity/bluetooth/find-bluetooth-devices
// QUERY PAIRED DEVICES
public static Set<BluetoothDevice> queryPairedDevices (BluetoothAdapter bluetoothAdapter) {
Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
Log.d(TAG, "PairedDevices: " + pairedDevices.toString());
return pairedDevices;
}
// GET ALL NAMES AND MAC ADDRESSES
public static ArrayList<HashMap<String, String>> getNamesAndMacAddresses(Set<BluetoothDevice> bluetoothDevices) {
ArrayList<HashMap<String, String>> arrayList = new ArrayList<>();
if (bluetoothDevices.size() > 0) {
for (BluetoothDevice device : bluetoothDevices) {
String deviceName = device.getName();
String deviceMacAddress = device.getAddress();
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("deviceName", deviceName);
hashMap.put("deviceMacAddress", deviceMacAddress);
arrayList.add(hashMap);
}
}
Log.d(TAG, "NamesAndMacAddress: " + arrayList);
return arrayList;
}
// SOURCE FOR FIND BLE DEVICES: https://developer.android.com/guide/topics/connectivity/bluetooth/find-ble-devices#java
// SCAN BLE DEVICES
public static void scanBLEDevices (BluetoothLeScanner bluetoothLeScanner) {
int SCAN_DURATION = 10000;
if(!isScanning) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
isScanning = false;
bluetoothLeScanner.stopScan(bleScanCallback);
Log.d(TAG, "ScanBLEDevices: " + "stop scanning");
}
}, SCAN_DURATION);
isScanning = true;
bluetoothLeScanner.startScan(bleScanCallback);
Log.d(TAG, "ScanBLEDevices: " + "start scanning");
}
else {
isScanning = false;
bluetoothLeScanner.stopScan(bleScanCallback);
Log.d(TAG, "ScanBLEDevices: " + "stop scanning");
}
}
// BLE SCAN CALLBACK
private static ScanCallback bleScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
Log.d(TAG, "ScanCallback: " + result.getDevice());
}
};
}
HomeActivity.java:
package com.example.ui.home;
import androidx.appcompat.app.AppCompatActivity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.BluetoothLeScanner;
import android.os.Bundle;
import android.view.View;
import com.example.R;
import com.example.databinding.ActivityHomeBinding;
import com.example.util.AuthenticationHelper;
import com.example.util.BluetoothHelper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
public class HomeActivity extends AppCompatActivity implements View.OnClickListener {
ActivityHomeBinding binding;
Set<BluetoothDevice> pairedDevices;
ArrayList<HashMap<String, String>> arrayList;
BluetoothLeScanner bluetoothLeScanner;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityHomeBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setUpBluetooth();
binding.buttonKeluar.setOnClickListener(this);
}
@Override
protected void onStart() {
super.onStart();
AuthenticationHelper.checkHasTheUserLoggedIn(this);
}
@Override
public void onClick(View v) {
if(v.getId() == R.id.buttonKeluar) {
AuthenticationHelper.signOutUser(this);
}
}
private void setUpBluetooth() {
BluetoothHelper.android10BluetoothPermission(this, HomeActivity.this);
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothHelper.setupBluetooth(bluetoothAdapter);
BluetoothHelper.enableBluetooth(this, bluetoothAdapter);
pairedDevices = BluetoothHelper.queryPairedDevices(bluetoothAdapter);
arrayList = BluetoothHelper.getNamesAndMacAddresses(pairedDevices);
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
BluetoothHelper.scanBLEDevices(bluetoothLeScanner);
}
}
我将此代码添加到 android10BluetoothPermission()
类的 BluetoothHelper.java
函数中,但仍然无效。
if (ContextCompat.checkSelfPermission(context,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
activity,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
PERMISSION_CODE
);
}
if (ContextCompat.checkSelfPermission(context,
Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
activity,
new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
PERMISSION_CODE
);
}
答案 0 :(得分:1)
最后,我修改了我的代码,所以代码现在可以工作。 这是:
ScanAndPairActivity.java(我正在创建一个用于扫描和配对 BLE 设备的新活动):
package com.example.ui.scanandpair;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.example.R;
import com.example.databinding.ActivityScanAndPairBinding;
import com.example.entity.BLEDeviceEntity;
import java.util.ArrayList;
import java.util.List;
public class ScanAndPairActivity extends AppCompatActivity {
// GENERAL
String TAG = getClass().getSimpleName();
ActivityScanAndPairBinding binding;
// BLUETOOTH
int REQUEST_ENABLE_BT = 1;
boolean isScanning = false;
BluetoothAdapter bluetoothAdapter;
BluetoothLeScanner bluetoothLeScanner;
ScanAndPairAdapter scanAndPairAdapter;
List<BLEDeviceEntity> bleDeviceEntityList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityScanAndPairBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
android10BluetoothPermission();
scanAndPairAdapter = new ScanAndPairAdapter(this);
bleDeviceEntityList = new ArrayList<>();
}
// SOURCE FOR BLUETOOTH PERMISSION: https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#java
// CHECK FOR BLUETOOTH PERMISSION FOR ANDROID 10
private void android10BluetoothPermission () {
boolean isLocationPermissionRequired = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
boolean isBGLocationAccessNotGranted = false;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
isBGLocationAccessNotGranted = ContextCompat.checkSelfPermission(
this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED;
}
boolean isLocationAccessNotGranted = ContextCompat.checkSelfPermission(
this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED;
if (isLocationPermissionRequired && isBGLocationAccessNotGranted && isLocationAccessNotGranted) {
requestLocationPermission();
}
else {
setUpBluetooth();
}
}
private void requestLocationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder
.setTitle(getString(R.string.izinkan_lokasi))
.setMessage(getString(R.string.mohon_izinkan_aplikasi_mengakses_lokasi))
.setPositiveButton(getString(R.string.ya), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
makeLocationRequest();
}
});
alertDialogBuilder.show();
} else {
makeLocationRequest();
}
}
}
private void makeLocationRequest() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{"android.permission.ACCESS_FINE_LOCATION"}, 101);
}
}
// ON REQUEST PERMISSION RESULT
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch(requestCode) {
case 101:
if (grantResults.length != 0 && grantResults[0] == 0) {
setUpBluetooth();
}
break;
default:
Toast.makeText(this, getString(R.string.maaf_izin_lokasi_harus_disetujui), Toast.LENGTH_LONG).show();
}
}
private void setUpBluetooth() {
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
checkBluetooth(bluetoothAdapter);
enableBluetooth(this, bluetoothAdapter);
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
scanBLEDevices(bluetoothLeScanner);
}
// SOURCE FOR SET UP BLUETOOTH: https://developer.android.com/guide/topics/connectivity/bluetooth/setup#java
// GET BLUETOOTH ADAPTER
private void checkBluetooth (BluetoothAdapter bluetoothAdapter) {
if (bluetoothAdapter == null) {
Log.d(TAG, "Bluetooth: " + "bluetooth adapter is null");
}
else {
Log.d(TAG, "Bluetooth: " + "bluetooth is adapter not null");
}
}
// ENABLE BLUETOOTH
private void enableBluetooth (Activity activity, BluetoothAdapter bluetoothAdapter) {
Log.d(TAG, "Bluetooth: " + "bluetooth adapter is enabled: " + bluetoothAdapter.isEnabled());
if (!bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
Log.d(TAG, "Bluetooth: " + "bluetooth is enabled");
}
else {
Log.d(TAG, "Bluetooth: " + "bluetooth is already enabled");
}
}
// SOURCE FOR FIND BLE DEVICES: https://developer.android.com/guide/topics/connectivity/bluetooth/find-ble-devices#java
// SCAN BLE DEVICES
private void scanBLEDevices (BluetoothLeScanner bluetoothLeScanner) {
showProgressBar();
int SCAN_DURATION = 5000;
if(!isScanning) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
isScanning = false;
bluetoothLeScanner.stopScan(bleScanCallback);
Log.d(TAG, "ScanBLEDevices: " + "stop scanning");
populateDataToRecyclerView();
}
}, SCAN_DURATION);
isScanning = true;
showProgressBar();
bluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), bleScanCallback);
Log.d(TAG, "ScanBLEDevices: " + "start scanning");
}
else {
isScanning = false;
bluetoothLeScanner.stopScan(bleScanCallback);
Log.d(TAG, "ScanBLEDevices: " + "stop scanning");
populateDataToRecyclerView();
}
}
private void showProgressBar() {
binding.recylerViewFilm.setVisibility(View.GONE);
binding.progressBar.setVisibility(View.VISIBLE);
}
private void hideProgressBar() {
binding.recylerViewFilm.setVisibility(View.VISIBLE);
binding.progressBar.setVisibility(View.GONE);
}
private void populateDataToRecyclerView() {
hideProgressBar();
scanAndPairAdapter.setBleDeviceEntityList(bleDeviceEntityList);
scanAndPairAdapter.notifyDataSetChanged();
binding.recylerViewFilm.setLayoutManager(new LinearLayoutManager(ScanAndPairActivity.this));
binding.recylerViewFilm.setHasFixedSize(true);
binding.recylerViewFilm.setAdapter(scanAndPairAdapter);
scanAndPairAdapter.setOnItemClickCallback(new ScanAndPairAdapter.OnItemClickCallback() {
@Override
public void onItemClicked(BLEDeviceEntity bleDeviceEntity) {
Toast.makeText(
ScanAndPairActivity.this, "try to connect to " + bleDeviceEntity.getName(), Toast.LENGTH_SHORT).show();
}
});
}
// BLE SCAN CALLBACK
private ScanCallback bleScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
String deviceAddress = result.getDevice().getAddress();
String deviceName = result.getDevice().getName();
Log.d(TAG, "ScanCallback, deviceAddress: " + deviceAddress + ", deviceName: " + deviceName);
if(bleDeviceEntityList.size() == 0) {
bleDeviceEntityList.add(new BLEDeviceEntity(deviceAddress, deviceName));
}
else {
for(BLEDeviceEntity bleDeviceEntity : bleDeviceEntityList){
if(!bleDeviceEntity.getAddress().equals(deviceAddress)) {
bleDeviceEntityList.add(new BLEDeviceEntity(deviceAddress, deviceName));
}
}
}
Log.d(TAG, "bleDeviceEntityList: " + bleDeviceEntityList);
}
};
// SOURCE FOR LATEST ANDROID BLE APPLICATION SAMPLE: https://github.com/android/connectivity-samples/tree/main/BluetoothAdvertisementsKotlin
private static List<ScanFilter> buildScanFilters() {
List<ScanFilter> scanFilters = new ArrayList<>();
ScanFilter.Builder builder = new ScanFilter.Builder();
// Comment out the below line to see all BLE devices around you
// builder.setServiceUuid(Constants.Service_UUID);
scanFilters.add(builder.build());
return scanFilters;
}
private static ScanSettings buildScanSettings() {
ScanSettings.Builder builder = new ScanSettings.Builder();
builder.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER);
return builder.build();
}
}
感谢大家的帮助,特别感谢 Isaidamier 向我展示示例应用 here。