【发布时间】:2020-08-30 16:24:03
【问题描述】:
我遇到了一个问题。我尝试了 Stack Overflow 和其他网站的一些提示。
我想编写一个应用程序,它搜索周围的所有蓝牙设备,如果找到匹配的设备(MAC 地址作为参考),应用程序将启动连接。
因此,我编写了一个测试应用程序来测试发现功能。但不幸的是,在我的 Android 10 设备上开始发现过程存在一个大问题。我有一台装有 Android 4.1.2 (SDK 16) 的旧版三星 S3 Mini,我的代码运行良好。
在 Android 10 设备上,startDiscovery() 返回 false,与返回 true 的 Android 4 设备不同。他们在android开发者页面上说,如果发生错误,则返回值为false。 BroadcastReceiver 应该可以正常工作,因为Android 9 手机上的应用程序检测到设置中已启动蓝牙搜索。只有startDiscovery() 函数才是问题的全部(在我看来)。
在开始发现过程之前,我正在检查所有权限和蓝牙状态。但我认为,它不可能是书面代码,因为它可以在旧设备上完美运行。也许我对较新的设备缺少一些东西。
编辑
正如 Thomas Morris 下面解释的那样,在 Android 10 中,您需要用户打开位置信息。在 Android 9 或更低版本中,Thomas Morris 的回答是正确的,因为在所有低于 29 的 SDK 中,只需要权限而不需要启用位置服务。
是否有避免要求用户自己打开位置的解决方案?
这是我的 MainActivity:
public class MainActivity extends AppCompatActivity {
final String TAG = "MainActivity";
BluetoothAdapter bluetoothAdapter;
int status = 0; //0 = start discovering, 1 = cancel discovering
public static final int REQUEST_ACCESS_COARSE_LOCATION = 1;
public static final int REQUEST_ENABLE_BLUETOOTH = 11;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
registerReceiver(receiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
registerReceiver(receiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_STARTED));
registerReceiver(receiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED));
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
checkBluetoothState();
final Button test = findViewById(R.id.testbutton);
test.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(status == 0) {
if(bluetoothAdapter != null && bluetoothAdapter.isEnabled()) {
if (checkCoarseLocationPermission()) {
Boolean result = bluetoothAdapter.startDiscovery(); //start discovering and show result of function
Toast.makeText(getApplicationContext(), "Start discovery result: " + result, Toast.LENGTH_SHORT).show();
Log.d(TAG, "Start discovery: " + result);
test.setText("Stop");
status = 1;
}
}else{
checkBluetoothState();
}
}else{
Log.d(TAG,"Stop");
status = 0;
bluetoothAdapter.cancelDiscovery();
test.setText("Start");
}
}
});
checkCoarseLocationPermission();
}
private boolean checkCoarseLocationPermission() {
//checks all needed permissions
if(ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_ACCESS_COARSE_LOCATION);
return false;
}else{
return true;
}
}
private void checkBluetoothState() {
//checks if bluetooth is available and if it´s enabled or not
if(bluetoothAdapter == null){
Toast.makeText(getApplicationContext(), "Bluetooth not available", Toast.LENGTH_SHORT).show();
}else{
if(bluetoothAdapter.isEnabled()){
if(bluetoothAdapter.isDiscovering()){
Toast.makeText(getApplicationContext(), "Device is discovering...", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(getApplicationContext(), "Bluetooth is enabled", Toast.LENGTH_SHORT).show();
}
}else{
Toast.makeText(getApplicationContext(), "You need to enabled bluetooth", Toast.LENGTH_SHORT).show();
Intent enabledIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabledIntent, REQUEST_ENABLE_BLUETOOTH);
}
}
}
// Create a BroadcastReceiver for ACTION_FOUND.
private final BroadcastReceiver receiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Discovery has found a device. Get the BluetoothDevice
// object and its info from the Intent.
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String deviceName = device.getName();
String deviceHardwareAddress = device.getAddress(); // MAC address
Log.d(TAG,"Device found: " + deviceName + "|" + deviceHardwareAddress);
Toast.makeText(getApplicationContext(), "FOUND: " + deviceName + "|" + deviceHardwareAddress, Toast.LENGTH_SHORT).show();
}
if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
//report user
Log.d(TAG,"Started");
Toast.makeText(getApplicationContext(), "STARTED", Toast.LENGTH_SHORT).show();
}
if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
//change button back to "Start"
status = 0;
final Button test = findViewById(R.id.testbutton);
test.setText("Start");
//report user
Log.d(TAG,"Finished");
Toast.makeText(getApplicationContext(), "FINISHED", Toast.LENGTH_SHORT).show();
}
if(BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)){
final int extra = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,-1);
if(extra == (BluetoothAdapter.STATE_ON)) {
if (bluetoothAdapter.isDiscovering()) {
bluetoothAdapter.cancelDiscovery();
}
Boolean b = bluetoothAdapter.startDiscovery();
Toast.makeText(getApplicationContext(), "Start discovery" + b, Toast.LENGTH_SHORT).show();
}
}
}
};
@Override
protected void onDestroy() {
super.onDestroy();
if (bluetoothAdapter.isDiscovering()){
bluetoothAdapter.cancelDiscovery();
}
unregisterReceiver(receiver);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
super.onActivityResult(requestCode,resultCode,data);
if(requestCode == REQUEST_ENABLE_BLUETOOTH){
checkBluetoothState();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
super.onRequestPermissionsResult(requestCode,permissions,grantResults);
switch (requestCode){
case REQUEST_ACCESS_COARSE_LOCATION:
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
Toast.makeText(getApplicationContext(),"Permission granted",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(getApplicationContext(),"Permission denied",Toast.LENGTH_SHORT).show();
}
}
}
}
这是我的清单:
<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/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
【问题讨论】:
-
您在运行应用程序时是否允许权限。您需要进入应用程序设置并启用位置。然后打开定位和蓝牙。您还需要检查您的手机是否为所需的 API 级别。
-
谢谢,解决方案通常太简单了。该位置从未打开,因此始终仅启用蓝牙。现在我所要做的就是让应用程序打开位置。我请求了位置权限,这在应用程序设置中也是允许的,但我必须手动打开位置。如果我这样做,发现过程就可以正常工作。
-
我已经为您发布了更详细的答案,请您批准它以使其他用户受益。还添加了一些代码,它应该会给你一个弹出窗口。如果您必须重新安装应用程序,手动操作会很烦人。为什么不使用弹出窗口。
-
您正在扫描,所以我认为这不是问题,但对于像我这样在互联网上搜索 Android 10 问题的可怜人,我发现如果您在连接 Android 10 之前不扫描,您将获得 Gatt 133。我通过检查是否配对以及是否不进行扫描来解决问题。在测试前禁用/启用 BT 以确认。
标签: android bluetooth android-bluetooth