前言:
不知不觉一年又要到头了,每天上班,吃饭,睡觉三点一线的匆匆而过。最近一个同事买房了,年龄跟我差不多,想想现在的房价,现在的消费,再看下自己的月薪,完全不敢想象自己能在这个城市奋斗到什么样子。有时候在想自己是不是应该换行了,但是换行自己能做什么?适合做什么?这些感觉在脑海里面都是模糊的,想想都觉得自卑。
再说说目前android开发行业吧,确实现在移动端开发大大不如前几年了,各行各业的竞争逐渐变大,想要在行业里面脱颖而出,那必定是精英干将了,就android版本不断更新,技术不断优化,导致了大多数人跟不上节奏,随后而慢慢被抛在后面(我也是菜鸟),其实说这些我个人感觉就是多余的,只要做这个行业的都知道这个行情,总之一句话:对得起这份工作,对得起自己!
1.Ble4.0开发
对于蓝牙开发从开始入行就听说过,而真正应用到项目上很少,其实蓝牙开发流程就那么几点:
- 检查权限(蓝牙扫描的需要加定位权限,android6.0以上需要手动获取权限)
- 判断设备是否支持蓝牙(移动设备)
- 打开蓝
- 打开蓝牙后可以进行扫描了(蓝牙扫描最好不要放在oncreat()里面)
- 监听扫描结果,进行连接
- 连接回调获取到gatt状态,连接状态,断开状态,读写通知特性等(在获取设备特性的时候我们需要对其设置uuid下面代码中会提到)
对于这套流程我就列出我所遇到的坑,希望各位网友能帮忙看下:
- 时不时扫描不到设备
- 读写特性不回调
一套蓝牙从打开到对接大概就这些流程吧,当然其中有很多细节问题需要我们注意的,这里我就不列出来了,大家有什么问题,或者疑问可以提出来讨论,下面我就列出我最近写的一套ble4.0代码,特别提出如果有不好或者疑问的欢迎提出:
2.从打开蓝牙到对接数据
1.对于每一个功能开发我们应当首先想到的就是权限问题,对于android6.0以上对于权限问题更加严谨,需要我们在代码中申请一些权限,让用户手动获取,那么蓝牙这块具体需要哪些权限呐
在清单文件中:
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!-- Android6.0 蓝牙扫描才需要-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
代码中
if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
) {
requestPermissions(new String[]{Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_COARSE_LOCATION}, RequestCode_Bluetooth);
} else {
BluetoothIsopen(bluetoothAdapter);
}
/**
* 权限申请结果
*
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == RequestCode_Bluetooth) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED && grantResults[2] == PackageManager.PERMISSION_GRANTED) {
BluetoothIsopen(bluetoothAdapter);
} else {
Toast.makeText(mActivity, "蓝牙权限被拒绝,请手动开启", Toast.LENGTH_SHORT).show();
}
}
}代码申请中我把三项都申请了,其实只需要定位申请就行.
2.我们有了权限,就可以开始搞事了,搞事之前是不是得判断这个事情是否可以搞呐?没错接下来就是判断蓝牙是否可用,这里我把打开蓝牙也一起写下了
if (bluetoothAdapter == null) {
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
LogUtils.e("设备不支持蓝牙");
} else {
if (!bluetoothAdapter.isEnabled()) {
//--如果蓝牙没有打开就提示用户打开
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);
}
}
}
3.蓝牙也打开了,那么接下来就是重点了,开启扫描!!!我在网上看到有说开启扫描最好开启新线程,具体原因我也不清楚
new Thread(new Runnable() {
@Override
public void run() {
if (null != mBluetoothGatt) {
mBluetoothGatt.close();
}
//开始搜索
bluetoothAdapter.startLeScan(mLeScanCallback);
}
}).start();
4.扫描到蓝牙设备就可用开始连接了,那么我们怎么监听扫描到的设备呐,请看上面搜索中的mLeScanCallback参数,当扫描到设备会回调mLeScanCallback接口,这里我设置了adress连接,表示只连接某一个设备.
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (!TextUtils.isEmpty(device.getName())) {
if (device.getAddress().equals("0C:B2:B7:54:27:1C")) {
if (connect(device.getAddress()))
bluetoothAdapter.stopLeScan(mLeScanCallback);
else connect(device.getAddress());
}
}
}
});切换到主线程
5.现在我们扫描到了设备就可用进行连接了
public boolean connect(final String address) {
final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
if (device == null) {
Log.w("msg", "Device not found. Unable to connect.");
return false;
}
if (mBluetoothGatt != null) {
mBluetoothGatt.close();
mBluetoothGatt.disconnect();
}
//这里才是真正连接
mBluetoothGatt = device.connectGatt(mActivity, false, mGattCallback);
return true;
}
这里的mGattCallback参数就是我们连接的设备状态,读写通知特性等了,我们在这定义一个mGattCallback并实现接口回调
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
/**
* 当GATT客户端已连接到GATT服务器或者从GATT服务器断开连接
* 时回调。
*
* @param gatt
* @param status 连接或断开操作的状态。BluetoothGatt.GATT_SUCCESS表示操作成功
*
* @param newState 返回新的连接状态。 如 BluetoothProfile.STATE_DISCONNECTED或
* BluetoothProfile.STATE_CONNECTED
*/
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
//连接成功
if (newState == BluetoothProfile.STATE_CONNECTED) {
//连接成功后就去找出该设备中的服务
//但是这个方法是异步操作,在回调函数onServicesDiscovered中得到status,
// 通过判断status是否等于BluetoothGatt.GATT_SUCCESS来判断查找Service是否成功
mBluetoothGatt.discoverServices();
} //连接失败
else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (!bluetoothAdapter.isEnabled()) {
Toast.makeText(mActivity, "蓝牙关闭了", Toast.LENGTH_SHORT).show();
return;
}
}
});
mBluetoothGatt.connect();
}
}
/**
* 当远程设备的远程服务列表,特征和描述符已被更新,即已发现新服务时,调用回调。
*
* @param gatt
* @param status BluetoothGatt.GATT_SUCCESS 远程设备的远程服务列表可被发现
*/
@Override
public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
//找到服务了
if (status == BluetoothGatt.GATT_SUCCESS) {
//在这里可以对服务进行解析,寻找到你需要的服务
//--读取数据
readData(serviceUuid, characteristicUuid);
} else {
Log.w("msg", "onServicesDiscovered received: " + status);
}
}
//当读取设备时会回调该函数
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
Log.e("msg", "出来的数据:");
if (status == BluetoothGatt.GATT_SUCCESS) {
}
}
//当向设备Descriptor中写数据时,会回调该函数
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
System.out.println("onDescriptorWriteonDescriptorWrite = " + status + ", descriptor =" + descriptor.getUuid().toString());
}
//设备发出通知时会调用到该接口
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic
characteristic) {
if (characteristic.getValue() != null && characteristic.getUuid().equals(characteristicUuid)) {
byte[] bytes = characteristic.getValue();
StringBuffer sBuffer = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
String s = Integer.toHexString(bytes[i] & 0xff);
if (s.length() < 2)
sBuffer.append('0');
sBuffer.append(s + " ");
}
parseData(sBuffer.toString());(自定义解析数据函数)
}
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
System.out.println("rssi = " + rssi);
}
//当向Characteristic写数据时会回调该函数
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic
characteristic, int status) {
System.out.println("--------write success----- status:" + status);
}
这里每个函数回调都有详细说明,特别注意上面的 readData(serviceUuid, characteristicUuid);这个方法就是设置特性的,没有这个方法上面的读写通知函数就不会回调了,我们需要设备方提供一个服务uuid,一个特性uuid
public void readData(UUID serviceUuid, UUID characteristicUuid) {
BluetoothGattService service = mBluetoothGatt.getService(serviceUuid);
if (service == null) {
return;
}
BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicUuid);
if (characteristic == null) {
return;
}
// 开启新数据接收通知
mBluetoothGatt.setCharacteristicNotification(characteristic, true);
List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors();
for (BluetoothGattDescriptor dp : descriptors) {
//设置特征的属性
dp.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(dp);
mBluetoothGatt.readDescriptor(dp);
}
// //--调用此方法会回调onCharacteristicRead
// mBluetoothGatt.readCharacteristic(characteristic);
// mBluetoothGatt.writeCharacteristic(characteristic);
}
从上面看出我们设置了通知特性,要传入两个uuid,然后当设备有数据的时候就会回调onCharacteristicChanged()函数,接下来就是数据解析等等处理了,到了这里ble4.0一套流程就基本完事了.
上面我设置了读写特性,但是无论怎么设置都不回调onCharacteristicRead(),与onDescriptorWrite()函数,找了半天也找不出什么原因,欢迎提出,感激不尽。