接着上一篇Android4.42-Settings源代码分析之蓝牙模块Bluetooth(上)

继续蓝牙模块源代码的研究

THREE。蓝牙模块功能实现

switch的分析以及本机蓝牙重命名和可见性的分析见上一篇,接下来进行第三章第三部分的介绍:关于蓝牙远程设备列表的载入。

假设没有看过,建议看看上一篇关第一章蓝牙的布局,有助于理解

3>,设备列表的载入

由于这部分代码非常多。所以在介绍时先说一下思路。程序首先通过底层的BluetoothAdapter的getBondedDevices()方法获取到已配对的设备列表,获取到列表后将数据缓存在List<CachedBluetoothDevice>中进行备份,当蓝牙界面启动后会从缓存中读取数据并显示已配对设备列表mPairedDevicesCategory,在扫描附近可用设备时会对缓存中的数据进行添加或者删除,并将数据显示在可用设备列表mAvailableDevicesCategory。而且程序会实时监听远程设备的状态变化,进行对设备列表的添加或删除。

设备列表的载入基本上就是这些,接下来挨个介绍

i>。调用底层代码获取可用设备列表并进行缓存

这部分代码的书写在BluetoothEventManager.java文件里,获取已配对设备列表的代码定义例如以下。

  1. boolean readPairedDevices() {
  2. //mLocalAdapter是将BluetoothAdapter映射到本地,其内部代码不再书写,获取到已配对设备
  3.  Set<BluetoothDevice> bondedDevices = mLocalAdapter.getBondedDevices();
  4. if (bondedDevices == null) {
  5. return false;
  6. }
  7. boolean deviceAdded = false;
  8. for (BluetoothDevice device : bondedDevices) {
  9. //这一步调用的是设备缓存列表的管理类CachedBluetoothDeviceManager中的方法findDevice
  10. //用于检查缓存列表中是否已经存在该device,若存在就将device返回,若不存在就返回null
  11. CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
  12. if (cachedDevice == null) {
  13. //假设缓存列表中没有该设备就调用管理类CachedBluetoothDeviceManager中的addDevice
  14. //将设备加入到缓存列表中
  15. cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
  16.  //将设备更新到屏幕上
  17. dispatchDeviceAdded(cachedDevice);
  18. deviceAdded = true;
  19. }
  20. }
  21. return deviceAdded;
  22. }

该方法在两个地方调用,一个是当本地蓝牙BluetoothAdapter开启后调用。一个就是当远程设备BluetoothDevice的状态发生改变时调用

例如以下,是在LocalBluetoothProfileManager.java文件里的代码,在蓝牙开启后会调用例如以下代码读取已配对的设备

  1. void setBluetoothStateOn() {
  2. ParcelUuid[] uuids = mLocalAdapter.getUuids();
  3. if (uuids != null) {
  4.  
  5. updateLocalProfiles(uuids);
  6. }
  7. mEventManager.readPairedDevices();
  8. }

当远程设备发生改变时会发送ACTION_BOND_STATE_CHANGED的广播,在注冊的handler中调用readPairedDevices()方法读取配对设备。监听广播的代码在BluetoothEventManager.java中。

事实上。在进行扫描后,获取的设备列表与可配对设备列表缓存在一起,这部分在介绍扫描处介绍

ii>,设备列表载入到屏幕

如今不论是已配对设备或是附近可用设备均缓存在同一列表,所以两个列表的载入类似。附近可用设备列表显示时会有一个progress。所以在构造preferenceGroup对象时有所差别,另一个差别就是设备的状态。通过底层的BluetoothDevice类中的getBondState()来获取远程设备的配对状态来区分。

设备列表的载入为BluetoothSettings中,已配对设备列表为mPairedDevicesCategory。附近可用设备列表为mAvailableDevicesCategory,均为PreferenceCategory对象,载入时调用的是BluetoothSettings.java中的addDeviceCategory(PreferenceGroup
preferenceGroup,int titleId,BluetoothDeviceFilter.Filter filter)方法。

已配对设备设置的过滤器为BluetoothDeviceFilter.BONDED_DEVICE_FILTER

附近可用设备设置的过滤器为BluetoothDeviceFilter.UNBONEDE_DEVICE_FILTER

  1. private void addDeviceCategory(PreferenceGroup preferenceGroup, int titleId,
  2. BluetoothDeviceFilter.Filter filter) {
  3. //设置preferenceGroup的标题
  4. preferenceGroup.setTitle(titleId);
  5. //为PreferenceScreen加入preferenceGroup,注意此时preferenceGroup里为空没有不论什么的preference
  6. getPreferenceScreen().addPreference(preferenceGroup);
  7. //设置过滤器,调用的是DeviceListPreferenceFragment中方法
  8. setFilter(filter);
  9. //调用DeviceListPreferencFragment中的方法。讲preferenceGroup传过去,方便对其操作
  10. setDeviceListGroup(preferenceGroup);
  11. //调用DeviceListPreferenceFragment中的方法
  12. addCachedDevices();
  13. //将preference设置为可点击的状态
  14. preferenceGroup.setEnabled(true);
  15. }

addCachedDevices()代码例如以下

  1. void addCachedDevices() {
  2. //用于获取到缓存列表的复制
  3.  Collection<CachedBluetoothDevice> cachedDevices =
  4. mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
  5. for (CachedBluetoothDevice cachedDevice : cachedDevices) {
  6. //该方法用于将设备显示出来
  7. onDeviceAdded(cachedDevice);
  8. }
  9. }

onDeviceAdded(cachedDevice)代码例如以下

  1. public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
  2. if (mDevicePreferenceMap.get(cachedDevice) != null) {
  3. return;
  4. }
  5.  
  6. // Prevent updates while the list shows one of the state messages
  7. if (mLocalAdapter.getBluetoothState() != BluetoothAdapter.STATE_ON) return;
  8. //这就是过滤器的作用了。首先过滤出要求的设备,要求已配对或者是附近可用设备
  9. //列表过滤后。就能够载入出来了
  10.  if (mFilter.matches(cachedDevice.getDevice())) {
  11. createDevicePreference(cachedDevice);
  12. }
  13. }

关于matches方法能够查看BluetoothDeviceFilter.java文件,不同的过滤器相应于不同的内部类。这些内部类实现了内部接口的matches方法。对BluetoothDevice的配对状态进行匹配,比方,过滤已经配对的蓝牙设备过滤器相应的内部类例如以下

  1. //注。Filter为BluetoothDeviceFilter的内部接口
  2. private static final class BondedDeviceFilter implements Filter {
  3. public boolean matches(BluetoothDevice device) {
  4. return device.getBondState() == BluetoothDevice.BOND_BONDED;
  5. }
  6. }

当对缓存列表进行过滤后,符合条件的就会调用createDevicePreference(cachedDevice)方法进行载入出来

  1. void createDevicePreference(CachedBluetoothDevice cachedDevice) {
  2. //构造preference对象
  3. BluetoothDevicePreference preference = new BluetoothDevicePreference(
  4. getActivity(), cachedDevice);
  5. //在该方法对preference进行初始化,可按需实现
  6. initDevicePreference(preference);
  7. //将preference显示出来
  8. mDeviceListGroup.addPreference(preference);
  9. mDevicePreferenceMap.put(cachedDevice, preference);
  10. }

设备列表的载入就到这儿。总结一下就是,对preferenceGroup总体的管理,诸如preference的增删该查操作,位于DeviceListPreferenceFragment.java文件里,可是对于preferenceGroup内部的preference的显示UI状态诸如title、summary、icon等,不在该类中而是在BluetoothDevicePreference.java中进行处理。从构造的preference对象就能够看出。

iii>。设备列表的改变

当设备状态发生变化时设备列表的显示也要发生变化,诸如设备进行配对,取消配对等操作。在BluetoothEvenManager.java中对设备的状态进行监听并处理,在该类的构造方法中注冊了很多的监听器,监听蓝牙相关的变化,比方蓝牙状态改变ACTION_STATE_CHANGED等等。有须要的能够看下。

在这里简单说一下各种广播

  • BluetoothAdpater.ACTION_STATE_CHANGED :本机蓝牙状态发生了改变
  • BluetoothAdpater.ACTION_DISCOVERY_STARTED:開始扫描
  • BluetoothAdpater.ACTION_DISCOVERY_FINISHED:扫描结束
  • BluetoothDevice.ACTION_FOUND:发现远程蓝牙设备
  • BluetoothDevice.ACTION_DISAPPEARED:远程设备消失
  • BluetoothDevice.ACTION_NAME_CHANGED:远程设备蓝牙名称改变
  • BluetoothDevice.ACTION_BOND_STATE_CHANGED:远程设备连接状态改变
  • BluetoothDevice.ACTION_PAIRING_CANCLE:远程设备取消配对
  • BluetoothDevice.ACTION_CLASS_CHANGED:远程设备的蓝牙类已经改变
  • BluetoothDevice.ACTION_UUID:

很多其它关于蓝牙广播的内容能够參考在线文档 http://www.android-doc.com/reference/android/bluetooth/BluetoothDevice.html

程序中已经为这些广播注冊了监听器,当接收到广播后作出对应动作,对列表即可改动

首先是对缓存列表进行更改。然后再对显示列表进行更改。

4>,蓝牙搜索附近可用设备

搜索功能流程例如以下:首先检測蓝牙是否开启,假设开启检測是否正在搜索。假设正在搜索则不做处理,假设未开启搜索则开启搜索

程序中的设置是假设蓝牙未开启或者正在搜索的话搜索设备button不可用。假设强制搜索是否正在播放音乐等。直接搜索。程序中设置的SCAN_EXPIRATION_MS为5分钟,有一种情况是搜索已经结束,可是时间没有5分钟,假设是非强制搜索在这样的情况下将不开启搜索。

  1. void startScanning(boolean force) {
  2. // Only start if we're not already scanning
  3. if (!mAdapter.isDiscovering()) {
  4. if (!force) {
  5. // Don't scan more than frequently than SCAN_EXPIRATION_MS,
  6. // unless forced
  7. if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {
  8. return;
  9. }
  10.  
  11. // If we are playing music, don't scan unless forced.
  12. A2dpProfile a2dp = mProfileManager.getA2dpProfile();
  13. if (a2dp != null && a2dp.isA2dpPlaying()) {
  14. return;
  15. }
  16. }
  17.  
  18. if (mAdapter.startDiscovery()) {
  19. mLastScan = System.currentTimeMillis();
  20. }
  21. }
  22. }

在搜索过程中发现设备会发送广播。程序会在广播处理代码中对缓存列表以及显示列表进行更新。

当開始扫描时发送扫描開始的广播,handler进行处理。当扫描接触时也是下列handler进行处理。仅仅是started为false

  1. private class ScanningStateChangedHandler implements Handler {
  2. private final boolean mStarted;
  3. //開始扫描时传入的为true
  4. ScanningStateChangedHandler(boolean started) {
  5. mStarted = started;
  6. }
  7. public void onReceive(Context context, Intent intent,
  8. BluetoothDevice device) {
  9. synchronized (mCallbacks) {
  10. for (BluetoothCallback callback : mCallbacks) {
  11. //调用DeviceListPreferenceFragment.java中的方法显示扫描指示progress
  12. callback.onScanningStateChanged(mStarted);
  13. }
  14. }
  15. //首先更新缓存列表。然后对显示列表进行排序更新显示。
  16. //排序规则代码在CachedBluetoothDevice.java中
  17. mDeviceManager.onScanningStateChanged(mStarted);
  18. //该方法用于保存開始扫描的时间
  19.  LocalBluetoothPreferences.persistDiscoveringTimestamp(context);
  20. }
  21. }

当扫描的过程中发现远程设备时处理例如以下

  1. private class DeviceFoundHandler implements Handler {
  2. public void onReceive(Context context, Intent intent,
  3. BluetoothDevice device) {
  4. //获取到蓝牙的信号强度,默觉得Short类型的最小值-2的15次方
  5. short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
  6. //获取到远程设备的类型
  7. BluetoothClass btClass = intent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS);
  8. //获取到远程设备的name
  9. String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
  10. //获取到远程设备后检測是否在缓存列表中,若有就返回设备。若没有返回null
  11. CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
  12. if (cachedDevice == null) {
  13. //将设备加入到缓存列表中
  14. cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
  15. // callback to UI to create Preference for new device
  16. //将加入的设备更新到显示列表
  17. dispatchDeviceAdded(cachedDevice);
  18. }
  19. //缓存device的信号强度。设备类型。name
  20. cachedDevice.setRssi(rssi);
  21. cachedDevice.setBtClass(btClass);
  22. cachedDevice.setName(name);
  23. //在这里不是设置可见性。与列表的排序相关
  24. cachedDevice.setVisible(true);
  25. }
  26. }

5>,蓝牙配对

设备列表中包含已配对设备、未配对设备、已连接设备等,当点击preference时会首先推断处于哪个状态。然后去进行下一个状态。假设没有配对,就进行配对

配对程序例如以下,在进行配对时首先检查远程设备是否正在配对,假设是,就返回true,假设没有在配对就现将本机的蓝牙配对状态设为true表示正在配对。紧接着停止蓝牙的扫描操作。与远程设备进行配对。配对成功后进行自己主动连接

  1. //该方法返回true代表正在进行配对操作,若返回false则表示配对操作失败弹出失败弹窗
  2. boolean startPairing() {
  3. //首先查看一下。远程设备是否正在配对。假设正在配对就返回true,
  4.  if(mLocalAdapter.checkPairingState() == true)
  5. {
  6. return true;
  7. }
  8. //将本机蓝牙适配器的配对状态设为true
  9. mLocalAdapter.setPairingState(true);
  10. // Pairing is unreliable while scanning, so cancel discovery
  11. //假设本机蓝牙正在进行扫描蓝牙的操作。则停止该操作,由于该操作会堵塞
  12. if (mLocalAdapter.isDiscovering()) {
  13. mLocalAdapter.cancelDiscovery();
  14. }
  15. //调用framework层的方法。推断远程蓝牙设备能否够配对以及请求配对是否超时,
  16. //假设能够配对就把远程蓝牙设备的配对状态设置为正在配对
  17.  if (!mDevice.createBond()) {
  18. //假设与远程蓝牙设备创建配对失败则将本机蓝牙配对状态设为false
  19. mLocalAdapter.setPairingState(false);
  20. return false;
  21. }
  22. //配对之后是否进行自己主动连接,true为自己主动进行连接
  23. mConnectAfterPairing = true; // auto-connect after pairing
  24. return true;
  25. }

6>。蓝牙连接

在进行连接前首先推断是否已经配对了,假设没有配对就会进行配对,取消连接的操作,若已经配对了则进行设备连接

  1. void connect(boolean connectAllProfiles) {
  2. //假设没有配对。就进行配对,而且退出连接的方法
  3. if (!ensurePaired()) {
  4. return;
  5. }
  6. //获取到系统启动到如今的时间间隔
  7. mConnectAttempted = SystemClock.elapsedRealtime();
  8. //从英语中能够看出意思是在连接时不重置定时器
  9. connectWithoutResettingTimer(connectAllProfiles);
  10. }

接下来看一下connectWithoutResettingTimer(connectAllProfiles)方法的代码

  1. private void connectWithoutResettingTimer(boolean connectAllProfiles) {
  2. // Try to initialize the profiles if they were not.
  3. //本机蓝牙与远程设备通信的配置规范,假设没有配置文件则不能进行通信
  4. //配置规范指定所使用的蓝牙通信协议。用户界面格式等等
  5. if (mProfiles.isEmpty()) {
  6. Log.d(TAG, "No profiles. Maybe we will connect later");
  7. return;
  8. }
  9.  
  10. // Reset the only-show-one-error-dialog tracking variable
  11. //当我们去连接多个设备错误发生时我们仅仅想显示一个错误对话框,
  12. mIsConnectingErrorPossible = true;
  13.  
  14. int preferredProfiles = 0;
  15. for (LocalBluetoothProfile profile : mProfiles) {
  16.  
  17. if (connectAllProfiles ?
  18.  
  19. profile.isConnectable() : profile.isAutoConnectable()) {
  20.  
  21. if (profile.isPreferred(mDevice)) {
  22. ++preferredProfiles;
  23. //连接设备。详细的能够查看关于profile的内容
  24. connectInt(profile);
  25.  
  26. }
  27. }
  28. }
  29. if (DEBUG) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
  30.  
  31. if (preferredProfiles == 0) {
  32. connectAutoConnectableProfiles();
  33. }
  34. }

FOUR,总结

1>,首先总结一下一些经常使用的frameworks层的蓝牙相关方法

i>,本地蓝牙相关

获取本地蓝牙适配器:BluetoothAdapter.getDefaultAdapter();

开启蓝牙:BluetoothAdapter----enable().

关闭蓝牙:BluetoothAdapter----disable().

重命名蓝牙:BluetoothAdapter----setName().

获取蓝牙名称:BluetoothAdapter----getName().

开启可检測性:BluetoothAdapter----setScanMode(BluetoothAdapter.

SCAN_MODE_CONNECTABLE_DISCOVERABLE,timeout).//当timeout设为0时表示永不超时

获取蓝牙状态:BluetoothAdapter----getState().

获取蓝牙所支持的uuid数组:BluetoothAdapter----getUuids().

获取已配对设备:BluetoothAdapter----getBoneDevices().

开启扫描:BluetoothAdapter----startDiscovery().

停止扫描:BluetoothAdapter----cancelDiscovery().

推断是否正在扫描:BluetoothAdapter----isDiscovery().

扫描低功耗BLE蓝牙设备:BluetoothAdapter----startLeScan(mLeScanCallBack).

停止对BLE设备的扫描:BluetoothAdapter----stopLeScan(mLeScanCallBack).

ii>,各种广播相关參考网址。这是一个API在线文档,解释的非常清楚

http://www.android-doc.com/reference/android/bluetooth/BluetoothDevice.html

2>,蓝牙模块源代码中涉及到的类

i>,BluetoothSettings.java:蓝牙界面的显示布局fragment。仅仅有布局相关,会对本机蓝牙的名字。可检測性进行实时更新,全部的点击事件的处理都在别处

ii>。DeviceListPreferenceFragment:远程设备列表的显示的更新。包含已配对列表和附近可用设备列表

iii>。BluetoothDevicePreference:列表中每一个设备的title。summary,icon的改动,包含设备的点击事件

iv>,CachedBluetoothDevice:管理远程设备,配对、连接

Android4.42-Setting源代码分析之蓝牙模块Bluetooth(下)的更多相关文章

  1. Android4.42-Settings源代码分析之蓝牙模块Bluetooth(上)

    继上一篇Android系统源代码剖析(一)---Settings 接着来介绍一下设置中某个模块的源代码.本文依然是基于Android4.42源代码进行分析,分析一下蓝牙模块的实现.建议大致看一下关于S ...

  2. Android4.42-Settings源代码分析之蓝牙模块Bluetooth总体实现(总)

    本文为博主原创,转载请注明出处:http://blog.csdn.net/zrf1335348191/article/details/50995466 蓝牙相关代码已在另两篇文章中介绍,有须要的能够查 ...

  3. MapReduce源代码分析之LocatedFileStatusFetcher

    LocatedFileStatusFetcher是MapReduce中一个针对给定输入路径数组,使用配置的线程数目来获取数据块位置的有用类. 它的主要作用就是利用多线程技术.每一个线程相应一个任务.每 ...

  4. Android使用BLE(低功耗蓝牙,Bluetooth Low Energy)

    背景 在学习BLE的过程中,积累了一些心得的DEMO,放到Github,形成本文.感兴趣的同学可以下载到源代码. github: https://github.com/vir56k/bluetooth ...

  5. 分析setting源代码获取sd卡大小

    分析setting源代码获取sd卡大小 android系统有一个特点,即开源,我们可以得到任何一个应用的源代码,比如我们不知道这样的android代码怎么写,我们可以打开模拟器里面的设置(settin ...

  6. nginx源代码分析--模块分类

    ngx-modules Nginx 基本的模块大致能够分为四类: handler – 协同完毕client请求的处理.产生响应数据.比方模块, ngx_http_rewrite_module, ngx ...

  7. [android] 分析setting源代码获取SD卡大小

    保存文件到sd卡需要判断sd卡的大小,通过查看android系统的自带应用的源代码,得到方法,sdk下面的source是sdk的源代码,包含的是android.Jar下面的所有class的源代码.在a ...

  8. Zepto核心模块源代码分析

    一.Zepto核心模块架构 Zepto核心模块架构图 该图展示了Zepto核心模块架构代码的组织方式.主要分为私有变量.函数和暴露给用户的所有api. Zepto核心模块架构代码 该图展示了Zepto ...

  9. 转:SDL2源代码分析

    1:初始化(SDL_Init()) SDL简介 有关SDL的简介在<最简单的视音频播放示例7:SDL2播放RGB/YUV>以及<最简单的视音频播放示例9:SDL2播放PCM>中 ...

随机推荐

  1. Python函数的装饰器

    函数的装饰器. 1. 装饰器 开闭原则: 对功能的扩展开放 对代码的修改是封闭 通用装饰器语法: def wrapper(fn): def inner(*args, **kwargs): # 聚合 & ...

  2. UIBarButtonSystemItem 样式

    使用时需要注意创建方式的区别: 01 typedef enum { 02     UIBarButtonSystemItemDone, 03     UIBarButtonSystemItemCanc ...

  3. I2C驱动框架(四)

    参考:I2C子系统之platform_driver初始化——I2C_adap_s3c_init() 在完成platform_device的添加之后,i2c子系统将进行platform_driver的注 ...

  4. I2C驱动框架(二)

    参考:I2C子系统之I2C bus初始化——I2C_init() 在linux内核启动的时候最先执行的和I2C子系统相关的函数应该是driver/i2c/i2c-core.c文件中的i2c_init( ...

  5. HUB、Switch、Router在OSI模型层次信息

    序 (HUB)集线器工作在局域网(LAN)环境,像网卡一样,应用于OSI参考模型第一层,因此又被称为物理层设备. Switch交换机工作在OSI第2层数据链路层 Router路由器工作在OSI第3层网 ...

  6. Ubuntu 16.04如何使用无线网卡上网

    我使用的无线网卡卡托型号是华为E8372h,网卡是普通电信卡(既可以打电话也可以上网). 按照“芯片朝上.缺口朝外.用最大卡”的方法将网卡装入卡托后,紧接着便将卡托插入笔记本对应的USB接口中. 在这 ...

  7. Spring,Mybatis,Springmvc框架整合项目(第一部分)

    一.说在前面的话 本篇博文实现一个注册登录小项目,使用spring,mybatis,springmvc框架进行整合,我们创建的是一个maven工程,主要是方便jar包版本的管理.项目使用eclispe ...

  8. !!注意!部署出现the requested resource is not available

    避免项目里重复包出现 同时tomcat的lib里避免重复包,也会出现requested resource isnot available!

  9. luogu1640 [SCOI2010]连续攻击游戏

    二分图匹配,一边是属性值,一边是武器 #include <iostream> #include <cstring> #include <cstdio> using ...

  10. IndiaHacks 2nd Elimination 2017 (unofficial, unrated mirror, ICPC rules)

    D. Airplane Arrangements time limit per test 2 seconds memory limit per test 256 megabytes input sta ...