Android BLE4.0(设备搜索)

接上一篇Android BLE4.0(基本知识),本篇记录在Android中的蓝牙4.0开发。要想与蓝牙设备进行通讯,首先要连接到相应的设备,连接到相应的设备之前,我们要能够搜索到它。所以我们先从找到设备开始。

1、申请权限

在Android中要想使用蓝牙,需要添加以下两个权限

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"/>

一般情况下,添加上面两个权限应该是可以了,但是老司机们都应该知道Android 6.0采用新的权限机制来保护用户的隐私,将权限分为Normal Permissions (不涉及用户隐私,不需要用户授权)Dangerous Permission (涉及用户隐私,使用时需要用户实时授权)两种。

蓝牙权限本身不属于用户隐私的权限,但是在Android 6.0之后要用蓝牙还需要添加一个模糊定位的权限

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

这个权限就属于隐私权限的范围了。。。。一股淡淡的忧伤。。。
所以想要兼容6.0还要在代码中检测权限:

1.1、Android 6.0 检测并申请权限
/*
 * 检测并申请权限
 */
private void checkBluetoothPermission() {
    if (Build.VERSION.SDK_INT >= 23) {
        //校验是否已具有模糊定位权限
        if (ContextCompat.checkSelfPermission(TYMposActivity.this,
                Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
           //申请模糊定位权限
           ActivityCompat.requestPermissions(TYMposActivity.this,
                   new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
                MY_PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION);
        } else {
           //具有权限
            connectBluetooth();
        }
    } else {
         //系统不高于6.0直接执行
         connectBluetooth();
    }
}

这里用到了两个API

  • ContextCompat.checkSelfPermission
    主要用于检测某个权限是否已经被授予,
    返回值为PackageManager.PERMISSION_DENIED
    或者是PackageManager.PERMISSION_GRANTED。当返回DENIED就需要进行申请授权了

  • ActivityCompat.requestPermissions
    这是个异步方法,有三个参数
    第一个参数是Context,这个不多说;
    第二个参数是需要申请的权限的字符串数组;
    第三个参数为requestCode,主要用于回调的时候检测。
    可以从方法名requestPermissions以及第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框 逐一 询问用户是否授权。

1.2、授权返回处理:

对授权返回值进行处理,有点类似于startActivityForResult

@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION: {
            // 如果请求被取消,则结果数组为空.
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //已授权
            } else {
                // 未授权
            }
            return;
        }
    }
}

关于6.0的权限申请到此结束,另外补充一下:
如果想声明你的app只为具有BLE的设备提供,在manifest文件中包括:

<uses-feature android:name="android.hardware.bluetooth_le" 
android:required="true"/>

但如果想让你的app提供给那些不支持BLE的设备,需要在manifest中包括上面代码并设置required="false",然后在运行时可以通过使用PackageManager.hasSystemFeature()确定BLE的可用性。

2、检查设备是否支持蓝牙

注意:Android系统版本在4.3及以上才能使用蓝牙4.0。
使用此检查确定BLE是否支持在设备上,然后你可以有选择性禁用BLE相关的功能

if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    Toast.makeText(this, "设备不支持蓝牙4.0", Toast.LENGTH_SHORT).show();
    finish();
}

3、蓝牙搜索

大致步骤

  • **获取BluetoothManager **
  • 获取BluetoothAdapter
  • 调用BluetoothAdapter.startLeScan开始扫描设备

获取BluetoothManager:

BluetoothManager manager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);

获取BluetoothAdapter
获取BluetoothAdapter有两种方式:

  • 第一种:
BluetoothAdapter adapter = manager.getAdapter();
  • 第二种
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 

扫描附近BLE设备
在开始扫描之前先打开蓝牙,打开之前需要先判断蓝牙是否已经打开:

/**
 * 蓝牙是否打开
 *
 * @return
 */
public boolean isBleOpen() {
   if (adapter == null) {
      return false;
   }
   return adapter.isEnabled();
}

打开蓝牙有两种方式

  • 跳转到系统的界面,手动打开
/**
 * 系统打开蓝牙
 */
public void sysOpenBLE(Activity activity, int requestCode) {
   Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
   activity.startActivityForResult(intent, requestCode);
}
  • 自动打开蓝牙
/**
 * 打开蓝牙
 */
public boolean openBLE() {
   if (adapter == null) {
      return false;
   }
   return adapter.enable();
}

确保蓝牙打开之后开始扫描设备:
通过adapter.startLeScan()方法来进行扫描。这里有两个方法:

/**
 *
 */
public boolean startLeScan(LeScanCallback callback) {
    return startLeScan(null, callback);
}

/**
 * @param serviceUuids Array of services to look for
 * 需要过滤的UUID服务,扫描的时候,只返回与指定UUID相同的设备
 */
public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) {
    if (DBG) Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids));
    if (callback == null) {
        if (DBG) Log.e(TAG, "startLeScan: null callback");
        return false;
    }
    BluetoothLeScanner scanner = getBluetoothLeScanner();
    if (scanner == null) {
        if (DBG) Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner");
        return false;
    }
  ......
}

以上代码摘自Android源码,这两个不多说什么,重点在 serviceUuids这里,大家都知道每个蓝牙都有一个服务UUID,这个参数就是针对这个UUID进行过滤的,扫描返回结果的时候,只会返回与UUID相同的设备,另外一个LeScanCallback**,这个是返回搜索结果的回调,如果这个LeScanCallback不传,扫描是不会启动的。

BluetoothAdapter.LeScanCallback scanCallback = new BluetoothAdapter.LeScanCallback() {
   @Override
   public void onLeScan(BluetoothDevice bluetoothDevice, int rssi, byte[] scanRecord) {
     
   }
};

在这个扫描回调中,回调了三个参数:

  • BluetoothDevice

/**
 * Represents a remote Bluetooth device. A {@link BluetoothDevice} lets you
 * create a connection with the respective device or query information about
 * it, such as the name, address, class, and bonding state.
 * 蓝牙信息相关的类,可以获取蓝牙的名称,地址,已经绑定状态等
 */
public final class BluetoothDevice implements Parcelable {

.....
//绑定状态相关
/**
 * Indicates the remote device is not bonded (paired).
 * <p>There is no shared link key with the remote device, so communication
 * (if it is allowed at all) will be unauthenticated and unencrypted.
 */
public static final int BOND_NONE = 10;
/**
 * Indicates bonding (pairing) is in progress with the remote device.
 */
public static final int BOND_BONDING = 11;
/**
 * Indicates the remote device is bonded (paired).
 * <p>A shared link keys exists locally for the remote device, so
 * communication can be authenticated and encrypted.
 * <p><i>Being bonded (paired) with a remote device does not necessarily
 * mean the device is currently connected. It just means that the pending
 * procedure was completed at some earlier time, and the link key is still
 * stored locally, ready to use on the next connection.
 * </i> 
*/
public static final int BOND_BONDED = 12;

.....

//设备类型
/**
 * Bluetooth device type, Unknown
 */
public static final int DEVICE_TYPE_UNKNOWN = 0;
/**
 * Bluetooth device type, Classic - BR/EDR devices,传统
 */
public static final int DEVICE_TYPE_CLASSIC = 1;
/**
 * Bluetooth device type, Low Energy - LE-only ,低功耗
 */
public static final int DEVICE_TYPE_LE = 2;
/**
 * Bluetooth device type, Dual Mode - BR/EDR/LE 双模式
 */
public static final int DEVICE_TYPE_DUAL = 3;

............

}

其实就是蓝牙的设备的一个实体类。没什么可以说的。

  • rssi
    RSSI的值作为对远程蓝牙设备信号值,正常为负值; 值越大信号越强;
  • scanRecord
    远程设备提供的配对号,一般用不到。

小结

如果不出意外,到这里是可以扫描到周边的蓝牙设备了,对于蓝牙开发已经算是迈出了第一步,也有了一个初步了解,接下来记录怎么连接到一个搜索到的设备。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容