当前位置: 首页 > news >正文

Android5.0(Lollipop) BLE蓝牙4.0+浅析code(二)

作者:Bgwan
链接:https://zhuanlan.zhihu.com/p/23347612
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Android5.0(Lollipop) BLE蓝牙4.0+浅析code说明(二)

Android4.4 启用蓝牙相关的操作方法主要是,BluetoothAdapter.startleScan();方法,最新的Android 5.0已经弃用了改方法,谷歌官方已经改用了android.bluetooth.le类來处理BLE的操作,所以我建议还是用最新的接口开发。但是为了兼容以前的版本改方法还是可以使用,上篇日志已经提到我们可以加一个SDK版本的判断,这里不过多说明,

这次更新总共在le下面添加了6个scan的相关类(4个advertise相关类),下面开始介绍这个类的作用。

1.BLE centrial的代码

mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();

利用adapter来得到一个scanner对象,这句代码和Peripheral的getBluetoothLeAdvertiser基本类似,一般手机支持BLE都会支持Central,除非是本身就只是外设设备。

advertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();

如果是Peripheral 得到advertiser对象,那么我们可以广播设置一些参数,数据,加一个CallBack,如下:

advertiser.startAdvertising(settings, data, mAdvertiseCallback);

这里面的三个参数,不再本文导论范围,但是可以看一下

// TODO Auto-generated method stub
AdvertiseSettings settings = new AdvertiseSettings.Builder()
        .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
        .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
        .setConnectable(false).build();
ParcelUuid pUuid = new ParcelUuid(UUID.fromString("00001000-0000-1000-8000-00805f9b34fb"));
AdvertiseData data = new AdvertiseData.Builder()
        .setIncludeDeviceName(true)
        .addServiceUuid(pUuid)
        .addServiceData(pUuid,
"Data".getBytes(Charset.forName("UTF-8"))).build();
final AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
@Override
    public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
if (settingsInEffect != null) {
            Log.d(TAG, "onStartSuccess TxPowerLv=" + settingsInEffect.getTxPowerLevel() + " mode=" + settingsInEffect.getMode()
                    + " timeout=" + settingsInEffect.getTimeout());
        } else {
            Log.e(TAG, "onStartSuccess, settingInEffect is null");
        }
        Log.e(TAG, "onStartSuccess settingsInEffect" + settingsInEffect);

    }

@Override
    public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);

if (errorCode == ADVERTISE_FAILED_DATA_TOO_LARGE) {
            Toast.makeText(mContext, "Operation failed due to an internal error", Toast.LENGTH_LONG).show();
            Log.e(TAG, "Failed to start advertising as the advertise data to be broadcasted is larger than 31 bytes.");
        } else if (errorCode == ADVERTISE_FAILED_TOO_MANY_ADVERTISERS) {
            Toast.makeText(mContext, "Operation failed due to an internal error", Toast.LENGTH_LONG).show();
            Log.e(TAG, "Failed to start advertising because no advertising instance is available.");
        } else if (errorCode == ADVERTISE_FAILED_ALREADY_STARTED) {
            Toast.makeText(mContext, "Operation failed due to an internal error", Toast.LENGTH_LONG).show();
            Log.e(TAG, "Failed to start advertising as the advertising is already started");
        } else if (errorCode == ADVERTISE_FAILED_INTERNAL_ERROR) {
            Toast.makeText(mContext, "Operation failed due to an internal error", Toast.LENGTH_LONG).show();
            Log.e(TAG, "Operation failed due to an internal error");
        } else if (errorCode == ADVERTISE_FAILED_FEATURE_UNSUPPORTED) {
            Toast.makeText(mContext, "This feature is not supported on this platform", Toast.LENGTH_LONG).show();
            Log.e(TAG, "This feature is not supported on this platform");
        } else {
            Toast.makeText(mContext, "onStartFailure errorCode", Toast.LENGTH_LONG).show();
            Log.e(TAG, "onStartFailure errorCode" + errorCode);
        }
    }
};

它主要的作用就是,假如说你有一个嵌入式的外设,蓝牙设备名字CTH-1001,这个蓝牙设备你可以通过设置这些参数去过滤你想要的蓝牙设备,如果周围还有其他的蓝牙外设你的手机可能就搜索不到,这也是在Android 后来新加入的功能,为了方便开发者使用。

2,接下来就是Scan的动作了,Scan新的接口动作把Scan分为了两类,

第一类:直接给一个callback

public void startScan(java.util.List<android.bluetooth.le.ScanFilter> filters, android.bluetooth.le.ScanSettings settings, android.bluetooth.le.ScanCallback callback) 
/**
 * Start Bluetooth LE scan with default parameters and no filters. The scan results will be
 * delivered through {@code callback}.
 * <p>
 * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
 *
 * @param callback Callback used to deliver scan results.
 * @throws IllegalArgumentException If {@code callback} is null.
 */
public void startScan(final ScanCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("callback is null");
    }
    startScan(null, new ScanSettings.Builder().build(), callback);
}

直接搜索全部周围peripheral设备,当然这里你要填写callback,

第二类为这里加入了新的过滤条件。可以看到ScanFilter:

    /**
    * Start Bluetooth LE scan. The scan results will be delivered through {@code callback}.
    * <p>
* Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
*
* @param filters {@link ScanFilter}s for finding exact BLE devices.
* @param settings Settings for the scan.
* @param callback Callback used to deliver scan results.
* @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
*/
public void startScan(List<ScanFilter> filters, ScanSettings settings,
    final ScanCallback callback) {
    startScan(filters, settings, callback, null);
    }

这一种明显属于定制化的函数了,因为他需要我们输入过滤条件。这里的ScanFilter和ScanSettings又是两个scan类,当然这两个类的目的主要是为了有些人想单独为某个产品开发应用,把过滤条件加上,比如DeviceName或者某个Service UUID等等,就可以搜索出只针对特定Peripheral特性的设备,就像我们公司的CTH-1001类型的设备。

单独看这两个新的接口,可能有些人会迷惑,本来不久应该是这样吗?其实Andoid L之前,scan接口不是这样的,就只有上面两种中的一种方法,suo'y。

3,OKay ,讲解了scanLeDevice,这里看一下回调mScanCallBack具体怎么实现的,其实在上篇文章已经贴出来了,

private ScanCallback mScanCallback = new ScanCallback() {
@Override
        public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if (result == null || result.getDevice() == null
                    || TextUtils.isEmpty(result.getDevice().getName())) {
mText.setText("没有搜索到蓝牙设备");
//                return;
            }
//如果是外设则可能存在没有ServiceUuids
//            builder.append("/n").append(
//                    new String(result.getScanRecord().getServiceData(
//                            result.getScanRecord().getServiceUuids().get(0)),
//                            Charset.forName("UTF-8")));

            BluetoothDevice device = result.getDevice();
            Log.d(TAG, "Device name: " + device.getName());
            Log.d(TAG, "Device address: " + device.getAddress());
            Log.d(TAG, "Device service UUIDs: " + device.getUuids());
if (builder.toString().contains(device.getName())) {
            } else {
builder.append("\n" + device.getName() + "&" + device.getAddress() + "\n");
            }
            ScanRecord record = result.getScanRecord();
            Log.d(TAG, "Record advertise flags: 0x" + Integer.toHexString(record.getAdvertiseFlags()));
            Log.d(TAG, "Record Tx power level: " + record.getTxPowerLevel());
            Log.d(TAG, "Record device name: " + record.getDeviceName());
            Log.d(TAG, "Record service UUIDs: " + record.getServiceUuids());
            Log.d(TAG, "Record service data: " + record.getServiceData());

mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();

mText.setText("搜索结果,builder:" + builder.toString());
        }

看到没有,这里包含result就是onScanResult(int callbackType, ScanResult result)的返回值,而且对于如何解析的代码我也贴出来了,是不是应该感动一下。

最后,所有的操作完了,不要忘记stop一下,我们可以在OnDestory方法中停止,当然加一个postdelay,这里操作我写了一个方法直接在需要的地方调用。

mHandler.postDelayed(new Runnable() {
@Override
    public void run() {
        stopScanning();
    }
}, DEFAULT_SCAN_PERIOD);

停止方法,

 private void stopScanning() {
if (mBluetoothLeScanner != null) {
        Log.d(TAG, "Stop scanning.");
mBluetoothLeScanner.stopScan(mBleScanCallback);
    }
}

剩下的就是connectGatt了,下篇文章我会重点介绍,蓝牙之间如何连接,如果传递数据,可能会结合我们公司的项目来讲解,

对了,我猜大家肯定需要源码,等公司项目完了,就上传一个修改后的demo给大家,欢迎大家关注。

转载于:https://www.cnblogs.com/Free-Thinker/p/6419331.html

相关文章:

  • gulp自动化构建工具--压缩css、js、img、编译less、自动刷新一体--学习笔记
  • RPG难题
  • 很不错标签云js插件
  • Atitit 遍历文件夹算法 autoit attilax总结
  • Hive常用命令
  • ArcGIS Runtime SDK for WPF之测量距离和面积
  • hash随笔
  • ELASTIC API
  • 关于版本强制升级与非强制升级
  • Syntax error, insert } to complete MethodBody
  • css之鼠标cursor
  • template.js小小说明
  • 阿里云centos6.5下搭建javaWeb运行环境
  • CPA-IBE
  • H5移动端开发入门知识以及CSS的单位汇总与用法
  • Google 是如何开发 Web 框架的
  • Apache的基本使用
  • JavaScript/HTML5图表开发工具JavaScript Charts v3.19.6发布【附下载】
  • vue-cli3搭建项目
  • 从@property说起(二)当我们写下@property (nonatomic, weak) id obj时,我们究竟写了什么...
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 浏览器缓存机制分析
  • 每天10道Java面试题,跟我走,offer有!
  • 面试总结JavaScript篇
  • 线性表及其算法(java实现)
  • 详解NodeJs流之一
  • 再谈express与koa的对比
  • 阿里云重庆大学大数据训练营落地分享
  • 如何用纯 CSS 创作一个货车 loader
  • 数据库巡检项
  • # 执行时间 统计mysql_一文说尽 MySQL 优化原理
  • $redis-setphp_redis Set命令,php操作Redis Set函数介绍
  • (function(){})()的分步解析
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (二)换源+apt-get基础配置+搜狗拼音
  • (附源码)计算机毕业设计大学生兼职系统
  • (力扣)循环队列的实现与详解(C语言)
  • (三)uboot源码分析
  • (四)c52学习之旅-流水LED灯
  • (学习日记)2024.04.10:UCOSIII第三十八节:事件实验
  • (转载)CentOS查看系统信息|CentOS查看命令
  • .NET 分布式技术比较
  • .NET/C# 中你可以在代码中写多个 Main 函数,然后按需要随时切换
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .Net高阶异常处理第二篇~~ dump进阶之MiniDumpWriter
  • @ 代码随想录算法训练营第8周(C语言)|Day53(动态规划)
  • @autowired注解作用_Spring Boot进阶教程——注解大全(建议收藏!)
  • @test注解_Spring 自定义注解你了解过吗?
  • [ vulhub漏洞复现篇 ] Hadoop-yarn-RPC 未授权访问漏洞复现
  • [ 蓝桥杯Web真题 ]-布局切换
  • [1159]adb判断手机屏幕状态并点亮屏幕
  • [BZOJ1040][P2607][ZJOI2008]骑士[树形DP+基环树]
  • [BZOJ1178][Apio2009]CONVENTION会议中心
  • [C#7] 1.Tuples(元组)
  • [ChromeApp]指南!让你的谷歌浏览器好用十倍!