代码流程分析二:Settings-蓝牙分析-新增rawgelWhile分析二
2015-05-04 19:17
393 查看
一:流程图已经分析
二:代码的分析
进入蓝牙界面,点击做软键做出的处理是弹出一个新的界面->流程如下
onResume();
给listView设置适配器,他里面放的是查询出来的可用设备
从shardpreference中取的mac地址值,
查询设备,她的原理是定时查询10秒后停止查询,在这10秒之间就查询回调函数的设备,也就是一进来查询10秒停止查询。
然后发一个消息:如果mac地址不为空,空linearLayout可见,设置mac地址,设置连接。否则就是mac地址为空的话,显示listView,并且里面放适配器,隔1秒给自己发一次这个消息。
如果有了之前连接的设备,查询连接以后。上来显示的是linearLayout里面放的是地址,和显示的连接,点击,注销了
前提是之前连接过这个设备的时候。
情形一:什么都没做的时候,进入界面,会查询设备,如果设备开着,配对成功了,会显示设备的地址和discoonected,上面 会查询的圈圈,10S之后停止。
流程:onCreate-->onResume-->scanLeDevice-->定时10S查询,按理说走回调方法mBluetoothAdapter.startLeScan(mLeScanCallback);但是这个时候没走为什么?谁能告诉我,而且后面listView显示的设备的地址是怎么查出来的。bluetoothCODAService收到广播,停止查询。-->Handler_delayed.sendEmptyMessage(0);1S刷新一遍,看mac地址是空,因为没有保存过这个地址,刚上来。但是现在居然可以了走了回调函数。所以显示listView,显示listView需要适配器,设备地址和disconnected-->点击这个listView的时候会刷新适配器,删除设备就是listView是空的,设置linearlayout显示出来,显示设备地址和连接状态,同时发送广播将设备的信息发送过去。后续的操作,稍等。。。在情形六分析了
情形一:连接成功,按了返回,或者关机键,退出去。home还是会显示连接状态,现在进去之后会自动连接,也就是进去会显示已经连linearlayout
流程一没进去在home的时候:后台服务查询出来。
流程二进去之后:onCreate()-->onResume()-->scanDevices()查询设备(10S定时为什么没走回调函数)-->发送一个消息每秒发送一次给自己。现在说显示界面,因为之前保存过了mac地址,linearlayout是可见的里面mac_adress不为空,显示设备adress,显示connected-->查询设备那10S之内,开始查询,发送广播,bluetoothCODAService这个接受广播action是"BluetoothScan"停止查询,那么问题来了,为什么没走查询回调函数,为什么连接了之后进去也查询了,但是不走回调函数
情形二:显示的是设备地址,connected,这个时候点击,点击的是linearlayout,会消失什么都没有了,意思就是断开连接了null
流程:走的linearlayout的点击事件,将adress和adress_old值为null,就是没值了,不存值了。-->Handler_delayed.sendEmptyMessage(0);1S刷新一遍,看mac地址因为点击了你给成null了所以刷新的时候会显示listView但是listView没有数据,所以看见就是什么都没有。-->发送广播bluetoothCODAService这个接受广播"BluetoothDissconnected"停止查询,屏幕变暗就走onpause(),停止查询设备
情形三:屏幕亮了之后会查询转一圈,什么也不显示,按园设备没反映。
流程:走onresume()-->scanDevices()-->bluetoothCODAService这个接受广播action是"BluetoothScan"然后停止查询。那么问题来了为啥没走发送消息,是因为mac地址为null?一会验证。
情形四:这个时候点击scan查询按钮,同时需要按住圆的那个,会显示设备的地址,下面是disconnected,这个下面的是listView
流程:走onresume()-->scanDevices()-->bluetoothCODAService这个接受广播action是"BluetoothScan"然后停止查询。会继续走回调函数-->给listView中填充设备,为什么这个会走回调函数啊?
情形五:这个时候屏幕变暗,再次打开什么也没有就是走了onpase()断开了连接,停止查询了
情形六:当我们点击连接的时候,直接变成了连接状态。linearlayout,
这个时候会走回调,也就是说,回调函数是在显示listView的时候才调用,显示linearlayout的时候不调用回调函数
流程:点击listView-->发送广播-->设置linear可见。bluetoothCODAService接到广播“BluetoothConnect”是这个动作-->mGattCallback走回调函数-->
方法一:onConnectionStateChange第一条if(连接)停止查询设备,第二个if(连接)discoverServices()-->发送icon广播
方法二:onServicesDiscovered-->displayGattServices_Battery(getSupportedGattServices());走电池电量-->
方法三:onCharacteristicRead
方法四:onCharacteristicChanged-->displayData()处理数据第一个if里面处理的方法是:连上设备之后点下去,对讲,点击,长按。第二个if的意思是不点击,走的方法-->Handler_Battery.sendEmptyMessage(0)-->displayGattServices-->存了值put了adress值和adress_old了值
情形七:当我们之前已经连接好了,现在关机状态,开机之后home会显示已经连接状态
流程:单纯的是开启了个服务来管理的
开机走的一个广播接受者:BootCompletedReceiver会启动BluetoothCODAService这个服务---->服务走oncreate->onStart会判断之前存的old的mac地址值不为null的情况下-->
scanLeDevice-->走回调函数跟前面一样,区别是之前的回调函数中是给适配器中加了设备,因为是要考虑listView的情况,这个下面是做了判断,如果现在的mac地址值和保存的mac地址值一样就走连接的回调函数BluetoothGattCallback又回到了接收到广播之后的处理了,所以这个时候情形6一样了。将现在的设备的各种值什么的存储一遍。
以上就是基本的流程功能:
BUG1:会显示2个设备,一个是连接状态另一个是不连接状态,问题是假如是连接状态,我再点查询(10S),在10S内我把设备关了再开,同时点击停止查询。会显示断开,我再打开设备就是出现2个状态,点一个就又出现问题了。。。。连上以后开关设备,再点击查询就出来2个了。连接的情况下,点击查询将设备关了再开,会出现2个,但是过一会自己没了,再点击连接就连不上了。
BUG2:listView显示未连接,点击连接,马上断开,不会保存值,下次开机连接不上。值为空
解决办法:将linearlayout里面的old的值put为null这句话注掉,为什么注掉,因为之前连接好了,shard有个缓存的原理,避免她的失误就是存了,不要值为空,再存的时候还是之前的值
BUG3:当我连上设备之后,长按设备会发生对讲,但是如果这个时候关闭这个设备,或者,在手机端点击断开连接,或者关闭蓝牙,这三种情况都是出现的问题是只走了按下的down事件,没走up上的事件,处理的方法:
先将下面pttkey的值保存起来,然后在断开设备的那个地方走这句话
else if(newState == BluetoothProfile.STATE_DISCONNECTED){
Log.v("lwn","state2="+BluetoothProfile.STATE_DISCONNECTED);
editor.putString("RadgerWheel_Connect_Address", "null");
editor.commit();
//[liuweinan][2015-5-7][add][S]
if(ptt_key_==1){
Intent intent2 = new Intent("com.android.bluetooth.support.BTPTTKey");
Log.v("lwn","zoul up");
intent2.putExtra("event_action", KeyEvent.ACTION_UP);
sendBroadcast(intent2);
}
//[liuweinan][2015-5-7][add][E]
}就是给ptt发送广播,告诉它将没有发送的up广播发送过去。
同理在接收的那,received蓝牙开关和断开连接都会给这个服务发送广播消息,所以在这处理同样的代码就可以。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
再分析RadgerWheel_Connect_Address的作用:是当前的设备
RadgerWheel_Connect_Address_old的作用:用处是,关机之后回来,还能继续连上。是保存了当前的设备的值,保存了就一直保存着,不给它值为null,就算断开了也是前面那个管着,值一直保存着,如果有新的设备进来会直接保存新的设备的值。
再说最上面的导航条是怎么来的?但是为什么这个菜单按钮是加到了右上脚?
Android2.3或更低的版本会在每次Menu打开的时候调用一次onPrepareOptionsMenu().
Android3.0及以上版本默认menu是打开的,所以必须调用invalidateOptionsMenu()方法,然后系统将调用onPrepareOptionsMenu()执行update操作。
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
if (!mScanning) {
menu.findItem(R.id.menu_stop).setVisible(false);
menu.findItem(R.id.menu_scan).setVisible(true);
menu.findItem(R.id.menu_refresh).setActionView(null);
menu.findItem(R.id.menu_refresh).setVisible(false);只显示查询
} else {
menu.findItem(R.id.menu_stop).setVisible(true);
menu.findItem(R.id.menu_scan).setVisible(false);
menu.findItem(R.id.menu_refresh).setVisible(true);
menu.findItem(R.id.menu_refresh).setActionView(
R.layout.actionbar_indeterminate_progress);显示停止显示进度条
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_scan:
mLeDeviceListAdapter.clear();查询操作
scanLeDevice(true);
break;
case R.id.menu_stop:
scanLeDevice(false);停止操作
break;
}
return true;
}
新加的内容,就是服务连接那块:
分类: Android
一、关键概念:
Generic
Attribute Profile (GATT)
通过BLE连接,读写属性类小数据的Profile通用规范。现在所有的BLE应用Profile都是基于GATT的。
Attribute
Protocol (ATT)
GATT是基于ATT
Protocol的。ATT针对BLE设备做了专门的优化,具体就是在传输过程中使用尽量少的数据。每个属性都有一个唯一的UUID,属性将以characteristics and services的形式传输。
Characteristic
Characteristic可以理解为一个数据类型,它包括一个value和0至多个对次value的描述(Descriptor)。
Descriptor
对Characteristic的描述,例如范围、计量单位等。
Service
Characteristic的集合。例如一个service叫做“Heart
Rate Monitor”,它可能包含多个Characteristics,其中可能包含一个叫做“heart rate measurement"的Characteristic。
二、角色和职责:
Android设备与BLE设备交互有两组角色:
中心设备和外围设备(Central
vs. peripheral);
GATT
server vs. GATT client.
Central
vs. peripheral:
中心设备和外围设备的概念针对的是BLE连接本身。Central角色负责scan
advertisement。而peripheral角色负责make advertisement。
GATT
server vs. GATT client:
这两种角色取决于BLE连接成功后,两个设备间通信的方式。
举例说明:
现有一个活动追踪的BLE设备和一个支持BLE的Android设备。Android设备支持Central角色,而BLE设备支持peripheral角色。创建一个BLE连接需要这两个角色都存在,都仅支持Central角色或者都仅支持peripheral角色则无法建立连接。
当连接建立后,它们之间就需要传输GATT数据。谁做server,谁做client,则取决于具体数据传输的情况。例如,如果活动追踪的BLE设备需要向Android设备传输sensor数据,则活动追踪器自然成为了server端;而如果活动追踪器需要从Android设备获取更新信息,则Android设备作为server端可能更合适。
三、权限及feature:
和经典蓝牙一样,应用使用蓝牙,需要声明BLUETOOTH权限,如果需要扫描设备或者操作蓝牙设置,则还需要BLUETOOTH_ADMIN权限:
<uses-permission
android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
除了蓝牙权限外,如果需要BLE
feature则还需要声明uses-feature:
<uses-feature
android:name="android.hardware.bluetooth_le" android:required="true"/>
按时required为true时,则应用只能在支持BLE的Android设备上安装运行;required为false时,Android设备均可正常安装运行,需要在代码运行时判断设备是否支持BLE
feature:
//
Use this check to determine whether BLE is supported on the device. Then
// you can selectively disable BLE-related features.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
四、启动蓝牙:
在使用蓝牙BLE之前,需要确认Android设备是否支持BLE
feature(required为false时),另外要需要确认蓝牙是否打开。
如果发现不支持BLE,则不能使用BLE相关的功能。如果支持BLE,但是蓝牙没打开,则需要打开蓝牙。
打开蓝牙的步骤:
1、获取BluetoothAdapter
BluetoothAdapter是Android系统中所有蓝牙操作都需要的,它对应本地Android设备的蓝牙模块,在整个系统中BluetoothAdapter是单例的。当你获取到它的示例之后,就能进行相关的蓝牙操作了。
获取BluetoothAdapter代码示例如下:
//
Initializes Bluetooth adapter.
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
注:这里通过getSystemService获取BluetoothManager,再通过BluetoothManager获取BluetoothAdapter。BluetoothManager在Android4.3以上支持(API
level 18)。
2、判断是否支持蓝牙,并打开蓝牙
获取到BluetoothAdapter之后,还需要判断是否支持蓝牙,以及蓝牙是否打开。
如果没打开,需要让用户打开蓝牙:
private
BluetoothAdapter mBluetoothAdapter;
...
// Ensures Bluetooth is available on the device and it is enabled. If not,
// displays a dialog requesting user permission to enable Bluetooth.
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
五、搜索BLE设备:
通过调用BluetoothAdapter的startLeScan()搜索BLE设备。调用此方法时需要传入BluetoothAdapter.LeScanCallback参数。
因此你需要实现 BluetoothAdapter.LeScanCallback接口,BLE设备的搜索结果将通过这个callback返回。
由于搜索需要尽量减少功耗,因此在实际使用时需要注意:
1、当找到对应的设备后,立即停止扫描;
2、不要循环搜索设备,为每次搜索设置适合的时间限制。避免设备不在可用范围的时候持续不停扫描,消耗电量。
搜索的示例代码如下:
/**
* Activity for scanning and displaying available BLE devices.
*/
public class DeviceScanActivity extends ListActivity {
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler;
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
...
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
...
}
...
}
如果你只需要搜索指定UUID的外设,你可以调用 startLeScan(UUID[],
BluetoothAdapter.LeScanCallback)方法。
其中UUID数组指定你的应用程序所支持的GATT
Services的UUID。
BluetoothAdapter.LeScanCallback的实现示例如下:
private
LeDeviceListAdapter mLeDeviceListAdapter;
...
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
注意:搜索时,你只能搜索传统蓝牙设备或者BLE设备,两者完全独立,不可同时被搜索。
六、连接GATT Server:
两个设备通过BLE通信,首先需要建立GATT连接。这里我们讲的是Android设备作为client端,连接GATT
Server。
连接GATT
Server,你需要调用BluetoothDevice的connectGatt()方法。此函数带三个参数:Context、autoConnect(boolean)和BluetoothGattCallback对象。调用示例:
mBluetoothGatt
= device.connectGatt(this, false, mGattCallback);
函数成功,返回BluetoothGatt对象,它是GATT
profile的封装。通过这个对象,我们就能进行GATT Client端的相关操作。BluetoothGattCallback用于传递一些连接状态及结果。
BluetoothGatt常规用到的几个操作示例:
connect()
:连接远程设备。
discoverServices()
: 搜索连接设备所支持的service。
disconnect():断开与远程设备的GATT连接。
close():关闭GATT
Client端。
readCharacteristic(characteristic)
:读取指定的characteristic。
setCharacteristicNotification(characteristic,
enabled) :设置当指定characteristic值变化时,发出通知。
getServices()
:获取远程设备所支持的services。
等等。
注:
1、某些函数调用之间存在先后关系。例如首先需要connect上才能discoverServices。
2、一些函数调用是异步的,需要得到的值不会立即返回,而会在BluetoothGattCallback的回调函数中返回。例如discoverServices与onServicesDiscovered回调,readCharacteristic与onCharacteristicRead回调,setCharacteristicNotification与onCharacteristicChanged回调等。
以上就是新增功能的所有流程,全部分析完毕。
二:代码的分析
进入蓝牙界面,点击做软键做出的处理是弹出一个新的界面->流程如下
onResume();
给listView设置适配器,他里面放的是查询出来的可用设备
从shardpreference中取的mac地址值,
查询设备,她的原理是定时查询10秒后停止查询,在这10秒之间就查询回调函数的设备,也就是一进来查询10秒停止查询。
然后发一个消息:如果mac地址不为空,空linearLayout可见,设置mac地址,设置连接。否则就是mac地址为空的话,显示listView,并且里面放适配器,隔1秒给自己发一次这个消息。
如果有了之前连接的设备,查询连接以后。上来显示的是linearLayout里面放的是地址,和显示的连接,点击,注销了
前提是之前连接过这个设备的时候。
情形一:什么都没做的时候,进入界面,会查询设备,如果设备开着,配对成功了,会显示设备的地址和discoonected,上面 会查询的圈圈,10S之后停止。
流程:onCreate-->onResume-->scanLeDevice-->定时10S查询,按理说走回调方法mBluetoothAdapter.startLeScan(mLeScanCallback);但是这个时候没走为什么?谁能告诉我,而且后面listView显示的设备的地址是怎么查出来的。bluetoothCODAService收到广播,停止查询。-->Handler_delayed.sendEmptyMessage(0);1S刷新一遍,看mac地址是空,因为没有保存过这个地址,刚上来。但是现在居然可以了走了回调函数。所以显示listView,显示listView需要适配器,设备地址和disconnected-->点击这个listView的时候会刷新适配器,删除设备就是listView是空的,设置linearlayout显示出来,显示设备地址和连接状态,同时发送广播将设备的信息发送过去。后续的操作,稍等。。。在情形六分析了
情形一:连接成功,按了返回,或者关机键,退出去。home还是会显示连接状态,现在进去之后会自动连接,也就是进去会显示已经连linearlayout
流程一没进去在home的时候:后台服务查询出来。
流程二进去之后:onCreate()-->onResume()-->scanDevices()查询设备(10S定时为什么没走回调函数)-->发送一个消息每秒发送一次给自己。现在说显示界面,因为之前保存过了mac地址,linearlayout是可见的里面mac_adress不为空,显示设备adress,显示connected-->查询设备那10S之内,开始查询,发送广播,bluetoothCODAService这个接受广播action是"BluetoothScan"停止查询,那么问题来了,为什么没走查询回调函数,为什么连接了之后进去也查询了,但是不走回调函数
情形二:显示的是设备地址,connected,这个时候点击,点击的是linearlayout,会消失什么都没有了,意思就是断开连接了null
流程:走的linearlayout的点击事件,将adress和adress_old值为null,就是没值了,不存值了。-->Handler_delayed.sendEmptyMessage(0);1S刷新一遍,看mac地址因为点击了你给成null了所以刷新的时候会显示listView但是listView没有数据,所以看见就是什么都没有。-->发送广播bluetoothCODAService这个接受广播"BluetoothDissconnected"停止查询,屏幕变暗就走onpause(),停止查询设备
情形三:屏幕亮了之后会查询转一圈,什么也不显示,按园设备没反映。
流程:走onresume()-->scanDevices()-->bluetoothCODAService这个接受广播action是"BluetoothScan"然后停止查询。那么问题来了为啥没走发送消息,是因为mac地址为null?一会验证。
情形四:这个时候点击scan查询按钮,同时需要按住圆的那个,会显示设备的地址,下面是disconnected,这个下面的是listView
流程:走onresume()-->scanDevices()-->bluetoothCODAService这个接受广播action是"BluetoothScan"然后停止查询。会继续走回调函数-->给listView中填充设备,为什么这个会走回调函数啊?
情形五:这个时候屏幕变暗,再次打开什么也没有就是走了onpase()断开了连接,停止查询了
情形六:当我们点击连接的时候,直接变成了连接状态。linearlayout,
这个时候会走回调,也就是说,回调函数是在显示listView的时候才调用,显示linearlayout的时候不调用回调函数
流程:点击listView-->发送广播-->设置linear可见。bluetoothCODAService接到广播“BluetoothConnect”是这个动作-->mGattCallback走回调函数-->
方法一:onConnectionStateChange第一条if(连接)停止查询设备,第二个if(连接)discoverServices()-->发送icon广播
方法二:onServicesDiscovered-->displayGattServices_Battery(getSupportedGattServices());走电池电量-->
方法三:onCharacteristicRead
方法四:onCharacteristicChanged-->displayData()处理数据第一个if里面处理的方法是:连上设备之后点下去,对讲,点击,长按。第二个if的意思是不点击,走的方法-->Handler_Battery.sendEmptyMessage(0)-->displayGattServices-->存了值put了adress值和adress_old了值
情形七:当我们之前已经连接好了,现在关机状态,开机之后home会显示已经连接状态
流程:单纯的是开启了个服务来管理的
开机走的一个广播接受者:BootCompletedReceiver会启动BluetoothCODAService这个服务---->服务走oncreate->onStart会判断之前存的old的mac地址值不为null的情况下-->
scanLeDevice-->走回调函数跟前面一样,区别是之前的回调函数中是给适配器中加了设备,因为是要考虑listView的情况,这个下面是做了判断,如果现在的mac地址值和保存的mac地址值一样就走连接的回调函数BluetoothGattCallback又回到了接收到广播之后的处理了,所以这个时候情形6一样了。将现在的设备的各种值什么的存储一遍。
以上就是基本的流程功能:
BUG1:会显示2个设备,一个是连接状态另一个是不连接状态,问题是假如是连接状态,我再点查询(10S),在10S内我把设备关了再开,同时点击停止查询。会显示断开,我再打开设备就是出现2个状态,点一个就又出现问题了。。。。连上以后开关设备,再点击查询就出来2个了。连接的情况下,点击查询将设备关了再开,会出现2个,但是过一会自己没了,再点击连接就连不上了。
BUG2:listView显示未连接,点击连接,马上断开,不会保存值,下次开机连接不上。值为空
解决办法:将linearlayout里面的old的值put为null这句话注掉,为什么注掉,因为之前连接好了,shard有个缓存的原理,避免她的失误就是存了,不要值为空,再存的时候还是之前的值
BUG3:当我连上设备之后,长按设备会发生对讲,但是如果这个时候关闭这个设备,或者,在手机端点击断开连接,或者关闭蓝牙,这三种情况都是出现的问题是只走了按下的down事件,没走up上的事件,处理的方法:
先将下面pttkey的值保存起来,然后在断开设备的那个地方走这句话
else if(newState == BluetoothProfile.STATE_DISCONNECTED){
Log.v("lwn","state2="+BluetoothProfile.STATE_DISCONNECTED);
editor.putString("RadgerWheel_Connect_Address", "null");
editor.commit();
//[liuweinan][2015-5-7][add][S]
if(ptt_key_==1){
Intent intent2 = new Intent("com.android.bluetooth.support.BTPTTKey");
Log.v("lwn","zoul up");
intent2.putExtra("event_action", KeyEvent.ACTION_UP);
sendBroadcast(intent2);
}
//[liuweinan][2015-5-7][add][E]
}就是给ptt发送广播,告诉它将没有发送的up广播发送过去。
同理在接收的那,received蓝牙开关和断开连接都会给这个服务发送广播消息,所以在这处理同样的代码就可以。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
再分析RadgerWheel_Connect_Address的作用:是当前的设备
RadgerWheel_Connect_Address_old的作用:用处是,关机之后回来,还能继续连上。是保存了当前的设备的值,保存了就一直保存着,不给它值为null,就算断开了也是前面那个管着,值一直保存着,如果有新的设备进来会直接保存新的设备的值。
再说最上面的导航条是怎么来的?但是为什么这个菜单按钮是加到了右上脚?
Android2.3或更低的版本会在每次Menu打开的时候调用一次onPrepareOptionsMenu().
Android3.0及以上版本默认menu是打开的,所以必须调用invalidateOptionsMenu()方法,然后系统将调用onPrepareOptionsMenu()执行update操作。
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
if (!mScanning) {
menu.findItem(R.id.menu_stop).setVisible(false);
menu.findItem(R.id.menu_scan).setVisible(true);
menu.findItem(R.id.menu_refresh).setActionView(null);
menu.findItem(R.id.menu_refresh).setVisible(false);只显示查询
} else {
menu.findItem(R.id.menu_stop).setVisible(true);
menu.findItem(R.id.menu_scan).setVisible(false);
menu.findItem(R.id.menu_refresh).setVisible(true);
menu.findItem(R.id.menu_refresh).setActionView(
R.layout.actionbar_indeterminate_progress);显示停止显示进度条
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_scan:
mLeDeviceListAdapter.clear();查询操作
scanLeDevice(true);
break;
case R.id.menu_stop:
scanLeDevice(false);停止操作
break;
}
return true;
}
新加的内容,就是服务连接那块:
Android4.3 蓝牙BLE初步
分类: Android一、关键概念:
Generic
Attribute Profile (GATT)
通过BLE连接,读写属性类小数据的Profile通用规范。现在所有的BLE应用Profile都是基于GATT的。
Attribute
Protocol (ATT)
GATT是基于ATT
Protocol的。ATT针对BLE设备做了专门的优化,具体就是在传输过程中使用尽量少的数据。每个属性都有一个唯一的UUID,属性将以characteristics and services的形式传输。
Characteristic
Characteristic可以理解为一个数据类型,它包括一个value和0至多个对次value的描述(Descriptor)。
Descriptor
对Characteristic的描述,例如范围、计量单位等。
Service
Characteristic的集合。例如一个service叫做“Heart
Rate Monitor”,它可能包含多个Characteristics,其中可能包含一个叫做“heart rate measurement"的Characteristic。
二、角色和职责:
Android设备与BLE设备交互有两组角色:
中心设备和外围设备(Central
vs. peripheral);
GATT
server vs. GATT client.
Central
vs. peripheral:
中心设备和外围设备的概念针对的是BLE连接本身。Central角色负责scan
advertisement。而peripheral角色负责make advertisement。
GATT
server vs. GATT client:
这两种角色取决于BLE连接成功后,两个设备间通信的方式。
举例说明:
现有一个活动追踪的BLE设备和一个支持BLE的Android设备。Android设备支持Central角色,而BLE设备支持peripheral角色。创建一个BLE连接需要这两个角色都存在,都仅支持Central角色或者都仅支持peripheral角色则无法建立连接。
当连接建立后,它们之间就需要传输GATT数据。谁做server,谁做client,则取决于具体数据传输的情况。例如,如果活动追踪的BLE设备需要向Android设备传输sensor数据,则活动追踪器自然成为了server端;而如果活动追踪器需要从Android设备获取更新信息,则Android设备作为server端可能更合适。
三、权限及feature:
和经典蓝牙一样,应用使用蓝牙,需要声明BLUETOOTH权限,如果需要扫描设备或者操作蓝牙设置,则还需要BLUETOOTH_ADMIN权限:
<uses-permission
android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
除了蓝牙权限外,如果需要BLE
feature则还需要声明uses-feature:
<uses-feature
android:name="android.hardware.bluetooth_le" android:required="true"/>
按时required为true时,则应用只能在支持BLE的Android设备上安装运行;required为false时,Android设备均可正常安装运行,需要在代码运行时判断设备是否支持BLE
feature:
//
Use this check to determine whether BLE is supported on the device. Then
// you can selectively disable BLE-related features.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
四、启动蓝牙:
在使用蓝牙BLE之前,需要确认Android设备是否支持BLE
feature(required为false时),另外要需要确认蓝牙是否打开。
如果发现不支持BLE,则不能使用BLE相关的功能。如果支持BLE,但是蓝牙没打开,则需要打开蓝牙。
打开蓝牙的步骤:
1、获取BluetoothAdapter
BluetoothAdapter是Android系统中所有蓝牙操作都需要的,它对应本地Android设备的蓝牙模块,在整个系统中BluetoothAdapter是单例的。当你获取到它的示例之后,就能进行相关的蓝牙操作了。
获取BluetoothAdapter代码示例如下:
//
Initializes Bluetooth adapter.
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
注:这里通过getSystemService获取BluetoothManager,再通过BluetoothManager获取BluetoothAdapter。BluetoothManager在Android4.3以上支持(API
level 18)。
2、判断是否支持蓝牙,并打开蓝牙
获取到BluetoothAdapter之后,还需要判断是否支持蓝牙,以及蓝牙是否打开。
如果没打开,需要让用户打开蓝牙:
private
BluetoothAdapter mBluetoothAdapter;
...
// Ensures Bluetooth is available on the device and it is enabled. If not,
// displays a dialog requesting user permission to enable Bluetooth.
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
五、搜索BLE设备:
通过调用BluetoothAdapter的startLeScan()搜索BLE设备。调用此方法时需要传入BluetoothAdapter.LeScanCallback参数。
因此你需要实现 BluetoothAdapter.LeScanCallback接口,BLE设备的搜索结果将通过这个callback返回。
由于搜索需要尽量减少功耗,因此在实际使用时需要注意:
1、当找到对应的设备后,立即停止扫描;
2、不要循环搜索设备,为每次搜索设置适合的时间限制。避免设备不在可用范围的时候持续不停扫描,消耗电量。
搜索的示例代码如下:
/**
* Activity for scanning and displaying available BLE devices.
*/
public class DeviceScanActivity extends ListActivity {
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler;
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
...
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
...
}
...
}
如果你只需要搜索指定UUID的外设,你可以调用 startLeScan(UUID[],
BluetoothAdapter.LeScanCallback)方法。
其中UUID数组指定你的应用程序所支持的GATT
Services的UUID。
BluetoothAdapter.LeScanCallback的实现示例如下:
private
LeDeviceListAdapter mLeDeviceListAdapter;
...
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
注意:搜索时,你只能搜索传统蓝牙设备或者BLE设备,两者完全独立,不可同时被搜索。
六、连接GATT Server:
两个设备通过BLE通信,首先需要建立GATT连接。这里我们讲的是Android设备作为client端,连接GATT
Server。
连接GATT
Server,你需要调用BluetoothDevice的connectGatt()方法。此函数带三个参数:Context、autoConnect(boolean)和BluetoothGattCallback对象。调用示例:
mBluetoothGatt
= device.connectGatt(this, false, mGattCallback);
函数成功,返回BluetoothGatt对象,它是GATT
profile的封装。通过这个对象,我们就能进行GATT Client端的相关操作。BluetoothGattCallback用于传递一些连接状态及结果。
BluetoothGatt常规用到的几个操作示例:
connect()
:连接远程设备。
discoverServices()
: 搜索连接设备所支持的service。
disconnect():断开与远程设备的GATT连接。
close():关闭GATT
Client端。
readCharacteristic(characteristic)
:读取指定的characteristic。
setCharacteristicNotification(characteristic,
enabled) :设置当指定characteristic值变化时,发出通知。
getServices()
:获取远程设备所支持的services。
等等。
注:
1、某些函数调用之间存在先后关系。例如首先需要connect上才能discoverServices。
2、一些函数调用是异步的,需要得到的值不会立即返回,而会在BluetoothGattCallback的回调函数中返回。例如discoverServices与onServicesDiscovered回调,readCharacteristic与onCharacteristicRead回调,setCharacteristicNotification与onCharacteristicChanged回调等。
以上就是新增功能的所有流程,全部分析完毕。
相关文章推荐
- 代码流程分析二:Settings-蓝牙分析-新增rawgelWhile分析一
- 代码流程分析二:Settings-蓝牙分析-myDevice根preference分析
- 代码流程分析二:Settings-蓝牙分析-加载界面
- 代码流程分析二:Settings-蓝牙分析-switch开关分析
- 代码流程分析二:Settings-蓝牙分析-点击配对连接设备原理分析
- 代码流程分析二:Settings-蓝牙分析-搜索设备原理
- androoid framework学习之Settings的主界面的代码流程分析
- 代码流程分析一:Settings中默认值的流程-显示-自动旋转屏幕
- Android4.4蓝牙耳机HFP流程分析-3
- APScheduler代码流程简要分析
- A20启动代码流程分析
- 蓝牙之六-A2dp代码调用流程
- 【Android4.4蓝牙代码分析】- 蓝牙Enable过程
- 3.linux启动流程分析(代码分析)
- RPG游戏《黑暗之光》流程介绍与代码分析之(六):背包系统的实现(下)
- Android Lollipop (5.0) 原生代码 Settings 首页加载逻辑分析
- 从Alarm看Android上层UI到内核代码的流程分析
- linux_蓝牙驱动代码分析
- servlet代码分析-整个执行流程
- 通过rk 代码,分析android 及kernel 中audio 的控制以及binder的流程