Android 蓝牙发现 API 不适用于 Android 6.0

Posted

技术标签:

【中文标题】Android 蓝牙发现 API 不适用于 Android 6.0【英文标题】:Android Bluetooth Discovery API Isn't Working on Android 6.0 【发布时间】:2017-01-07 13:06:29 【问题描述】:

我做了一个应用程序,发现附近的蓝牙设备等。我可以得到蓝牙适配器,打开关闭进程,获取配对设备,发现进程。一切正常,但发现过程无法正常工作。

我在 5 种不同的真实设备上测试了我的应用。目前我意识到我的 android 版本有问题,但我不确定。

我的应用的发现过程正在进行中,

华为P7-安卓版本:4.4.2 Sony Xperia Z - Android 版本:5.1.1 三星 Galaxy J3 - Android 版本:5.1.1

我的应用的发现过程不正常,

三星 Galaxy S7 - Android 版本:6.0.1 三星 Galaxy J7 - Android 版本:6.0.1

MainActivity.java

package com.sphinxlike.bluetoothexample;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.Parcelable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;

import static java.lang.System.out;


public class MainActivity extends AppCompatActivity implements View.OnClickListener

    /* DECLERATIONS */
    // Button
    Button openBtn = null;                                      // Open button
    Button closeBtn = null;                                     // Close button
    Button listBtn = null;                                      // Paired devices list button
    Button searchBtn = null;                                    // Discovery new devices button

    // Bluetooth
    private BluetoothAdapter mBluetoothAdapter = null;          // Local bluetooth adapter variable
    private static final int REQUEST_ENABLE_BT = 1;             // Bluetooth open intent variable
    String mDeviceName = null;
    String mDeviceAddress = null;
    public static String EXTRA_ADDRESS = "device_address";

    // XML
    TextView tvDeviceName = null;                               // To show local bluetooth device's name
    TextView tvBluetoothState = null;                           // To show bluetooth state
    ListView lvDeviceList = null;                               // To list paired devices or discovered devices

    // Set, List, Array Adapter
    private Set<BluetoothDevice> mPairedDevicesSet = null;      // Paired devices Set
    private Set<BluetoothDevice> mNewDevicesSet = null;         // New devices Set
    ArrayAdapter mPairedDevicesAdapter = null;                  // Paired devices array adapter
    ArrayAdapter mNewDevicesAdapter = null;                     // New devices array adapter
    ArrayList bluetoothDeviceList = null;                       // Listing bluetooth devices
    private ArrayList<BluetoothDevice> btDeviceList = new ArrayList<BluetoothDevice>();

    // Bluetooth State Updater
    private Handler mHandler;                                   // Loop in UI

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

        // Initializes UI elements
        tvDeviceName = (TextView)findViewById(R.id.deviceName);
        tvBluetoothState = (TextView)findViewById(R.id.bluetoothState);
        openBtn = (Button)findViewById(R.id.openBluetooth);
        closeBtn = (Button)findViewById(R.id.closeBluetooth);
        listBtn = (Button)findViewById(R.id.listBluetooth);
        searchBtn = (Button)findViewById(R.id.searchBluetooth);
        lvDeviceList = (ListView)findViewById(R.id.deviceList);

        // Bluetooth adapter
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        // Button checks
        openBtn.setOnClickListener((View.OnClickListener) this);
        closeBtn.setOnClickListener((View.OnClickListener) this);
        listBtn.setOnClickListener((View.OnClickListener) this);
        searchBtn.setOnClickListener((View.OnClickListener) this);

        // To update bluetooth state text view
        tvBluetoothState.setText(String.valueOf(mBluetoothAdapter.getState()));


        // If the phone does not support bluetooth adapter
        if (mBluetoothAdapter == null) 
            tvBluetoothState.setText("Your phone has not Bluetooth Adapter");
            openBtn.setEnabled(false);
            closeBtn.setEnabled(false);
            listBtn.setEnabled(false);
            searchBtn.setEnabled(false);
        

        // Updater loop
        mHandler = new Handler();
        mHandler.post(mUpdate);
    

    // Updater Loop
    private Runnable mUpdate = new Runnable() 
        public void run() 
            getBluetoothState();
            mHandler.postDelayed(this, 500);
        
    ;

    // Button onClick method
    public void onClick(View v) 
        switch (v.getId()) 
            case R.id.openBluetooth:
                if (!mBluetoothAdapter.isEnabled()) 
                    Intent bluetoothAcIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                    startActivityForResult(bluetoothAcIntent, REQUEST_ENABLE_BT);
                 else 
                    Toast.makeText(getApplicationContext(), "Bluetooth is already open", Toast.LENGTH_SHORT).show();
                
                break;
            case R.id.closeBluetooth:
                if (mBluetoothAdapter.isEnabled()) 
                    mBluetoothAdapter.disable();
                 else 
                    Toast.makeText(getApplicationContext(), "Bluetooth is already close", Toast.LENGTH_SHORT).show();
                
                break;
            case R.id.listBluetooth:
                pairedDevicesList();
                break;
            case R.id.searchBluetooth:
                IntentFilter filter = new IntentFilter();

                filter.addAction(BluetoothDevice.ACTION_FOUND);
                filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
                filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);

                registerReceiver(mBroadcastReceiver, filter);
                mBluetoothAdapter.startDiscovery();

                break;
        
    ;

    @Override
    public void onDestroy() 
        unregisterReceiver(mBroadcastReceiver);

        super.onDestroy();
    

    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() 
        public void onReceive(Context context, Intent intent) 
            String action = intent.getAction();
            // Whenever a remote Bluetooth device is found
            if (BluetoothDevice.ACTION_FOUND.equals(action)) 
                // Get the BluetoothDevice object from the Intent
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                Toast.makeText(getApplicationContext(), device.getName() + ":" + device.getAddress(), Toast.LENGTH_LONG).show();
//                lvDeviceList.setAdapter(mPairedDevicesAdapter);     // Set list view elements with adapter elements
            
        
    ;

    // Show bluetooth state on UI
    public void getBluetoothState() 
        tvDeviceName.setText("Device Name: " + mBluetoothAdapter.getName());
        switch(mBluetoothAdapter.getState())                                 // Set bluetooth state text view
            case 10:    // STATE_OFF
                tvBluetoothState.setText("Bluetooth is closed");
                bluetoothDeviceList = new ArrayList();              // Initialize global array list variable that is declared
                mPairedDevicesAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, bluetoothDeviceList);     // Get array list and use in array adapter
                lvDeviceList.setAdapter(mPairedDevicesAdapter);     // Set list view elements with adapter elements
                break;
            case 11:    // STATE_TURNING_ON
                tvBluetoothState.setText("Bluetooth is opening");
                break;
            case 12:    // STATE_ON
                tvBluetoothState.setText("Bluetooth is opened");
                break;
            case 13:    // STATE_TURNING_OFF
                tvBluetoothState.setText("Bluetooth is closing");
                break;
        

        // If bluetooth adapter  -> Set buttons
        if (mBluetoothAdapter.isEnabled()) 
            // Enable listing button
            listBtn.setEnabled(true);
            searchBtn.setEnabled(true);
         else 
            // Disable listing button
            listBtn.setEnabled(false);
            searchBtn.setEnabled(false);
        
    

    // Get paired device list and adapt to list view
    public void pairedDevicesList()
    
        if (!mBluetoothAdapter.isEnabled()) 
            Toast.makeText(getApplicationContext(), "Open the Bluetooth", Toast.LENGTH_SHORT).show();
         else 
            mPairedDevicesSet = mBluetoothAdapter.getBondedDevices();       // Get paired devices
            bluetoothDeviceList = new ArrayList();                      // Initialize global array list variable that is declared

            if (mPairedDevicesSet.size()>0) 
                for (BluetoothDevice bt : mPairedDevicesSet)               // for-each loop
                    mDeviceName = bt.getName();
                    mDeviceAddress = bt.getAddress();
                    bluetoothDeviceList.add(mDeviceName + "\n" + mDeviceAddress);              // get the device name and add to array list object
                
             else if (mPairedDevicesSet.size()<=0)
                Toast.makeText(getApplicationContext(), "No paired devices", Toast.LENGTH_SHORT).show();
            
            mPairedDevicesAdapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1, bluetoothDeviceList);     // Get array list and use in array adapter
            lvDeviceList.setAdapter(mPairedDevicesAdapter);     // Set list view elements with adapter elements
        
    


activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_
    android:layout_
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.sphinxlike.bluetoothexample.MainActivity"
    android:orientation="vertical">

    <TextView
        android:layout_
        android:layout_
        android:hint="Device Name"
        android:id="@+id/deviceName"/>

    <LinearLayout
        android:layout_
        android:layout_
        android:orientation="horizontal"
        android:layout_marginTop="20dp">

        <Button
            android:layout_
            android:layout_weight="1"
            android:layout_
            android:id="@+id/openBluetooth"
            android:text="B. Open"
            android:textAllCaps="false"
            android:layout_gravity="center"
            android:gravity="center"/>
        <Button
            android:layout_
            android:layout_weight="1"
            android:layout_
            android:id="@+id/closeBluetooth"
            android:text="B. Close"
            android:textAllCaps="false"
            android:layout_gravity="center"
            android:gravity="center"/>
        <Button
            android:layout_
            android:layout_weight="1"
            android:layout_
            android:id="@+id/listBluetooth"
            android:text="List Paired"
            android:textAllCaps="false"
            android:layout_gravity="center"
            android:gravity="center"/>
        <Button
            android:layout_
            android:layout_weight="1"
            android:layout_
            android:id="@+id/searchBluetooth"
            android:text="Search"
            android:textAllCaps="false"
            android:layout_gravity="center"
            android:gravity="center"/>
    </LinearLayout>

    <TextView
        android:layout_
        android:layout_
        android:layout_marginTop="20dp"
        android:hint="Bluetooth State"
        android:id="@+id/bluetoothState"
        android:layout_gravity="center"
        android:gravity="center"/>

    <ListView
        android:layout_
        android:layout_
        android:id="@+id/deviceList">

    </ListView>
</LinearLayout>

【问题讨论】:

【参考方案1】:

首先,感谢@Tomasz Czura。我解决了多个运行时权限请求的问题。 Android Developer 6.0 Changes页面说:

要通过蓝牙和 Wi-Fi 扫描访问附近外部设备的硬件标识符,您的应用现在必须具有 ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION 权限:

所以,我为 AndroidManifest.XML 添加了权限:

<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" />

然后我像这样重新安排我的活动:

public class MainActivity extends AppCompatActivity 
...
    int ACTION_REQUEST_MULTIPLE_PERMISSION = 1;  // Any number

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        ...

        int pCheck = this.checkSelfPermission("Manifest.permission.ACCESS_FINE_LOCATION");
        pCheck += this.checkSelfPermission("Manifest.permission.ACCESS_COARSE_LOCATION");
        pCheck += this.checkSelfPermission("Manifest.permission.BLUETOOTH_ADMIN");
        pCheck += this.checkSelfPermission("Manifest.permission.BLUETOOTH");
        if (pCheck != 0) 
            this.requestPermissions(new String[]Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION,
            Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH, ACTION_REQUEST_MULTIPLE_PERMISSION);
        
    

成功了。

【讨论】:

【参考方案2】:

在 Android 6.0 中,您必须在运行时请求蓝牙权限 -

ActivityCompat.requestPermissions(..)

【讨论】:

但是我可以打开关闭蓝牙,获取蓝牙适配器并获取配对设备。他们不需要运行时许可吗?发现 api 是否需要运行时权限? 尝试请求位置权限android.permission.ACCESS_COARSE_LOCATION 您有任何错误吗?你的日志中有什么? 我在 OnCreate() 方法的底部使用了代码,try ActivityCompat.requestPermissions(MainActivity.this, new String[]Manifest.permission.BLUETOOTH,Manifest.permission.BLUETOOTH_ADMIN, REQUEST_ENABLE_BT); catch RuntimeException e) Log.e("Error on runtime request: ", e.getMessage()); 但无法得到错误信息?跨度>

以上是关于Android 蓝牙发现 API 不适用于 Android 6.0的主要内容,如果未能解决你的问题,请参考以下文章

Cordova 文件插件不适用于 android sdk 30

ios蓝牙api可以发现android设备吗?

android 中的 API 用于 Find me profile 中的蓝牙即时警报服务

Android 蓝牙 API 连接到多个设备

在 Android 12/API 31 中,地理围栏不适用于 IMMUTABLE 未决意图。为啥?

Seekbar 样式不适用于较低的 API