安卓找不到任何BLE设备

4
这里有一个问题。我已经开发了一个iOS应用程序来控制BLE LED设备(和其他一些东西)。一切工作都很顺利。现在,我想为Android开发相同的应用程序,在扫描BLE设备方面我已经失败了。我尝试了一些教程和示例代码,但无论我做什么,我都找不到任何设备。我正在使用Moto G4 Play进行开发。蓝牙功能可用,并且我可以在设置中配对设备,但它不能与我尝试过的任何示例代码/教程一起使用。例如这个: https://github.com/kaviles/BLE_Tutorials 我按照原样构建了此应用程序,但它无法找到任何内容。
因此,我从Playstore下载了一个BLE扫描仪,它可以正常工作并找到所有设备。
我知道没有示例代码很难说,但我已经尝试了很多次,而且我不确定是否错过了一些非常基本的东西。

2
检查应用程序的权限。检查SDK 23权限,因为您需要位置权限。确保位置服务已打开。除此之外,请检查您的代码是否正确! - zed
我设置了所有权限,但还是不起作用。根据这个链接:https://code.google.com/p/android/issues/detail?id=196485 看起来这是一个相当大的问题。 - Patricks
这不是问题,而是预期行为,因为BLE设备可以提供位置信息。您必须添加位置服务权限,并且如果您正在构建API 23+,则必须在运行时请求权限。 - zed
我指的是开发人员的问题。似乎有很多关于这个的抱怨。 - Patricks
1
清单权限必须由应用程序开发人员进行设置。关于运行时权限:https://developer.android.com/training/permissions/requesting.html - zed
显示剩余2条评论
9个回答

7

正如评论中所讨论的那样,如果您的targetSdk为23+或其他版本,则必须相应地设置权限。位置服务必须开启。

API 23+的清单权限:

<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-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>

检查蓝牙权限:

    public boolean hasBlePermissions() {
        if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION)
                        != PackageManager.PERMISSION_GRANTED) {
            return false;
        } else {
            return true;
        }
    }

使用以下步骤请求运行时权限:

public void requestBlePermissions(final Activity activity, int requestCode) {
    ActivityCompat.requestPermissions(activity,
            new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION},
            requestCode);
}

接下来,要检查来自OnRequestPermissionResult的授权结果:

public boolean checkGrantResults(String[] permissions, int[] grantResults) {
    int granted = 0;

    if (grantResults.length > 0) {
        for(int i = 0; i < permissions.length ; i++) {
            String permission = permissions[i];
            if (permission.equals(Manifest.permission.ACCESS_FINE_LOCATION) ||
                    permission.equals(Manifest.permission.ACCESS_COARSE_LOCATION)) {
                if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                    granted++;
                }
            }
        }
    } else { // if cancelled
        return false;
    }

    return granted == 2;
}

检查位置服务:

public boolean areLocationServicesEnabled(Context context) {
    LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
    try {
        return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) ||
                locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

requestBlePermissions() 方法体似乎请求位置权限而不是蓝牙权限。 - Rule

4
感谢您的好解释。现在我感觉很蠢,因为我真的不知道如何让它工作。
这是我的清单文件:
<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-feature android:name="android.hardware.location.gps" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

这是我的活动:

public class LightActivity extends AppCompatActivity {
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler;
private final static int REQUEST_ENABLE_BT = 1;

// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_light);

    if(hasBlePermissions() && areLocationServicesEnabled(this)) {
        final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();

        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, "Bluetooth low energy is not supported", Toast.LENGTH_SHORT).show();
            finish();
        }

        // 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()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        } else {
            scanLeDevice(true);
        }
    }
}

public boolean hasBlePermissions() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
        ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
        return true;
    } else {
        return false;
    }
}

public void requestBlePermissions(final Activity activity, int requestCode) {
    ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, requestCode);
}

public boolean checkGrantResults(String[] permissions, int[] grantResults) {
    int granted = 0;

    if (grantResults.length > 0) {
        for(int i = 0; i < permissions.length ; i++) {
            String permission = permissions[i];
            if (permission.equals(Manifest.permission.ACCESS_FINE_LOCATION) || permission.equals(Manifest.permission.ACCESS_COARSE_LOCATION)) {
                if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                    granted++;
                }
            }
        }
    } else { // if cancelled
        return false;
    }

    return granted == 2;
}

public boolean areLocationServicesEnabled(Context context) {
    LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

    try {
        return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

private void scanLeDevice(final boolean enable) {
    if (enable) {


        mScanning = true;
        mBluetoothAdapter.startLeScan(mLeScanCallback);
    } else {
        mScanning = false;
        mBluetoothAdapter.stopLeScan(mLeScanCallback);
    }
}

// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
        new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(final BluetoothDevice device, int rssi,
                         byte[] scanRecord) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Log.i("NEW DEVICE", device.getName());
            }
        });
    }
};

我发现 hasBlePermissions() 总是返回 false,即使在清单文件中的权限设置正确。

更新:已经搞定了。一旦你理解了它,它并不难。首先授予权限,然后扫描设备。以下是我在 onCreate 方法中更新的代码:

int permissionCheck = ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION);

if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
} else {
    if(areLocationServicesEnabled(this)) {
        mHandler = new Handler();

        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
            finish();
        }

        final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();

        scanLeDevice(true);
    }
}

对我不起作用。你确定这是对你有效的代码吗? - Sahar Millis
1
请看我上面更新的代码。我希望那是最重要的部分。我的代码自从上次发布以来改变了很多,所以我需要追溯问题出在哪里。 - Patricks

1
如果您使用https://github.com/kaviles/BLE_Tutorials构建项目,只需在MainActivity中获取Adapter后添加以下代码行:
    ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, 1001);

谢谢。显然,在较高版本的Android上,您需要同时拥有这两个权限。我之前只允许其中一个。 - Lheonair

0

我正在使用 Cordova 插件 cordova-plugin-ble-central,它与 Android 非常兼容。该插件需要以下权限:

ACCESS_COARSE_LOCATION

BLUETOOTH

BLUETOOTH_ADMIN

希望这能帮到你,


0

0

0

对于 Xamarin,API>=24 我找到了解决方法,可能不太直观,您需要 COARSE_LOCATION 权限才能让蓝牙正常工作。在 Xamarin 项目中,右键单击 > 选项 > 勾选蓝牙和位置权限的框 > 确定 --> 在设备上重新构建


0

我认为摩托罗拉设备存在问题。摩托罗拉设备将位置和GPS合并为一个称为“位置”的单一选项。在其他设备上,只要您的应用程序可以访问位置,蓝牙扫描就可以正常工作,但如果您使用摩托罗拉设备,则还需要启用位置(GPS)。这很奇怪也很令人困惑。


0

对于 Android 10,您还需要 ACCESS_BACKGROUND_LOCATION 权限

Kotlin 代码

    if (ContextCompat.checkSelfPermission(
                   baseContext,
                   Manifest.permission.ACCESS_BACKGROUND_LOCATION
               ) != PackageManager.PERMISSION_GRANTED) 
           {
               ActivityCompat.requestPermissions(
                   this@MainActivity,
                   arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
                   12)
           }

你有这方面的参考资料吗?据我所知,仅在Android 10中需要ACCESS_FINE_LOCATION。 - Emil
这些信息是错误的...在Android 10中,BLE不需要ACCESS_BACKGROUND_LOCATION权限。只有ACCESS_FINE_LOCATION权限就足够了。这个Android开发者页面只提到了FINE权限,我刚刚检查了一下,没有BACKGROUND权限也可以使用BLE。 - Fabio Goncalves
你有这方面的参考资料吗?据我所知,Android 10 只需要 ACCESS_FINE_LOCATION 权限。不幸的是,它并不起作用。 - Vivek Samele

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接