目录
今日科技快讯
2月21日凌晨,三星美国旧金山举行Galaxy Unpacked 2019新品发布会。正式发布年度旗舰Galaxy S10系列手机以及折叠手机Galaxy Fold,还推出了首款5G手机。
Galaxy Fold此前曾在2018年11月开发者大会上亮相,但当时并没宣布具体参数,今天这是它终于定妆出场。它的形态是两折的,仿佛一个钱包,外表是一块4.6英寸的狭长屏幕,打开后,内部是一块7.3英寸的屏幕。三星Galaxy S10系列一共包含三款机型,分别是S10、S10+、S10e。相比去年的S9系列,S10系列外观最大的变化在于采用Dynamic AMOLED屏幕设计,网友俗称“挖孔屏”,其中S10为6.1英寸2K分辨率双曲全视屏、S10+为6.4英寸2K分辨率双曲全视屏,S10e由于定位相比前两者更低,所以仍然使用1080P非双曲全视屏。(转郭霖大神的快讯)
介绍
蓝牙介绍:
BLE蓝牙的兴起主要因为近年来可穿戴设备的流行。由于传统蓝牙功耗高不能满足可穿戴设备对于续航的要求。所以大部分可穿戴设备采用蓝牙4.0,即BLE蓝牙技术。BLE(Bluetooth Low Energy)低功耗蓝牙,主要特点是快速搜索,快速连接,超低功耗保持连接和数据传输。
缺点:BLE蓝牙数据传输速率低,特别是在安卓开发过程,BLE蓝牙一包数据最多为20字节,因此安卓系统下最好不要使用BLE蓝牙传输大量数据。
之前我在创业公司工作的时候做过一款蓝牙开锁APP,当时刚拿到需求的时候各种懵逼,在网上百度了好几天都没有一点头目,因为都讲的牛头不对马嘴,换句话来说不是我想要的,根本满足不了我现在的需求,不知道从何入手,然后问身边各种蓝牙大牛以及技术大牛才稍微有一点头目,在这里给大家分享一下技术难点和各种坑,我直接上图:
流程大概是这样:我懒得画图了,用简单一句话来讲就是,房东分配钥匙-租客拿到权限-临时权限(各种权限....)-查看房子-下单
实战
在开始之前推荐几个蓝牙框架给大家学习:GitHub - dingjikerbo/Android-BluetoothKit: Android BLE蓝牙通信库,GitHub - litesuits/android-lite-bluetoothLE: BLE Framework. Based on Bluetooth 4.0. Based on callback. Extremely simple! Communication with BluetoothLE(BLE) device as easy as HTTP communication. Android低功耗蓝牙便捷操作框架,基于回调,完成蓝牙设备交互就像发送网络请求一样简单。
上面已经简单得介绍了一下项目的流程,现在直接上重点以第一个指令开始演示:
由于怕影响到公司业务,打上马赛克,你们可以根据自己的业务需求来实现功能
权限代码:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.nokelock.nokelockble">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="com.nokelock.service.BluetoothLeService"
android:enabled="true" />
<activity android:name=".LockManageActivity" android:screenOrientation="portrait"/>
</application>
</manifest>
扫描部分:
private void startScanDevice() {
if (isRefreshing) return;
isRefreshing = true;
BluetoothLeService bluetoothLeService = App.getInstance().getBluetoothLeService();
if (bluetoothLeService != null) {
final BluetoothAdapter bluetoothAdapter = bluetoothLeService.getmBluetoothAdapter();
if (bluetoothAdapter == null) {
isRefreshing = false;
return;
}
tvRefresh.setText("正在扫描...");
bluetoothDeviceList.clear();
adapterList.clear();
bleDeviceList.clear();
bluetoothAdapter.startLeScan(new UUID[]{SampleGattAttributes.bltServerUUID}, leScanCallback);
handler.postDelayed(new Runnable() {
@Override
public void run() {
tvRefresh.setText("扫描结束");
isRefreshing = false;
bluetoothAdapter.stopLeScan(leScanCallback);
}
}, 5000);
} else {
isRefreshing = false;
}
}
扫描回调:
这一块可以做处理比如我匹配到mac地址直接跳转到主页,你们可以做成那种扫描完以后放到adapter里面进行展示,这样更加直观明了。
private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
if (!bluetoothDeviceList.contains(device)) {
bluetoothDeviceList.add(device);
bleDevice = new BleDevice(device, scanRecord, rssi);
Log.e("TAG", "名字" + device.getName());
Log.e("TAG", "MAC" + device.getAddress());
//自动连接
if ("F0:0F:26:5D:95:E3".equals(device.getAddress())) {
Log.e("TAG", "ok");
App.getInstance().getBluetoothLeService().connect("F0:0F:26:5D:95:E3");
Intent intent = new Intent(MainActivity.this, LockManageActivity.class);
intent.putExtra("name", device.getName());
intent.putExtra("address", device.getAddress());
startActivity(intent);
}
bleDeviceList.add(bleDevice);
}
}
};
private void initWidget() {
comp = new SortComparator();
ListView listView = (ListView) findViewById(R.id.recycler_view);
tvRefresh = (TextView) findViewById(R.id.tv_refresh);
Button btRefresh = (Button) findViewById(R.id.bt_refresh);
adapter = new DeviceAdapter(this, adapterList);
listView.setAdapter(adapter);
new Thread(new DeviceThread()).start();
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
App.getInstance().getBluetoothLeService().getmBluetoothAdapter().stopLeScan(leScanCallback);
isRefreshing = false;
tvRefresh.setText("扫描结束");
BleDevice bluetoothDevice = adapterList.get(position);
String name = bluetoothDevice.getDevice().getName();
if (TextUtils.isEmpty(name)) {
name = "null";
}
String address = bluetoothDevice.getDevice().getAddress();
Intent intent = new Intent(MainActivity.this, LockManageActivity.class);
intent.putExtra("name", name);
intent.putExtra("address", address);
startActivity(intent);
}
});
btRefresh.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
refreshDevice();
}
});
}
private void refreshDevice() {
requestPermission(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 101);
}
连接到蓝牙以后呢,就比较简单了,现在要做的是蓝牙锁的读和写:
/**
* BLE通讯广播
*/
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
case SampleGattAttributes.ACTION_GATT_CONNECTED:
//链接
deviceStatus.setText("连接状态:已连接");
progressDialog.dismiss();
break;
case SampleGattAttributes.ACTION_GATT_DISCONNECTED:
//断开
progressDialog.dismiss();
deviceStatus.setText("连接状态:已断开");
count = 0;
openCount.setText("开锁次数:" + count);
break;
case SampleGattAttributes.ACTION_GATT_SERVICES_DISCOVERED:
//发现服务
handler.sendEmptyMessageDelayed(0, 2000);
break;
case SampleGattAttributes.ACTION_BLE_REAL_DATA:
parseData(intent.getStringExtra("data"));
break;
case BluetoothAdapter.ACTION_STATE_CHANGED:
Log.e(LockManageActivity.class.getSimpleName(), "state_changed");
break;
}
}
};
private void parseData(String value) {
byte[] values = HexUtils.hexStringToBytes(value);
byte[] x = new byte[16];
System.arraycopy(values, 0, x, 0, 16);
byte[] decrypt = BluetoothLeService.Decrypt(x, SampleGattAttributes.key);
String decryptString = HexUtils.bytesToHexString(decrypt).toUpperCase();
Log.e("TAG", "value:" + decryptString);
if (decryptString.startsWith("0102")) {//token
if (decrypt != null && decrypt.length == 16) {
if (decrypt[0] == 0x01 && decrypt[1] == 0x02) {
token[0] = decrypt[3];
token[1] = decrypt[4];
token[2] = decrypt[5];
token[3] = decrypt[6];
CHIP_TYPE = decrypt[7];
DEV_TYPE = decrypt[10];
deviceVersion.setText("当前版本:" + Integer.parseInt(decryptString.substring(16, 18), 16) + "." + Integer.parseInt(decryptString.substring(18, 20), 16));
handler.sendEmptyMessageDelayed(1, 1000);
}
}
handler.sendEmptyMessage(1);
} else if (decryptString.startsWith("0402")) {//电量
progressDialog.dismiss();
// if (decryptString.startsWith("020201ff")) {
// deviceCz.setText("获取电量失败");
// Log.e("TAG", "获取电量失败");
// } else {
String battery = decryptString.substring(10, 12);
deviceBattery.setText("当前电量:" + battery);
Log.e("TAG", "电量字符串" + battery);
Log.e("TAG", "当前电量" + Integer.parseInt(battery, 16));
// }
} else if (decryptString.startsWith("0202")) {//开锁
if (decryptString.startsWith("02020101")) {
deviceCz.setText("开锁失败");
} else {
count++;
deviceCz.setText("开锁成功");
openCount.setText("开锁次数:" + count);
}
}
基本一系列操作已经做完了,主要难点就是指令的读和写,然后在转成文档对应的编码就可以了,后面只需要按照文档里面的读和写操作就可以了,上面代码完全可以用设计模式优化一下,在这里只做演示,所以就没有那么优雅。
之前有过一篇蓝牙打印文章感兴趣请移步:Android蓝牙打印机,带你真正了解各种打印格式_outputstream.write([0x1b, 0x45, 0x00])-CSDN博客
练习
对照现有的蓝牙框架进行练习:GitHub - dingjikerbo/Android-BluetoothKit: Android BLE蓝牙通信库,GitHub - litesuits/android-lite-bluetoothLE: BLE Framework. Based on Bluetooth 4.0. Based on callback. Extremely simple! Communication with BluetoothLE(BLE) device as easy as HTTP communication. Android低功耗蓝牙便捷操作框架,基于回调,完成蓝牙设备交互就像发送网络请求一样简单。