Android从4.3(Api level 18)开始支持BLE的开发,本文记录了Android 4.4.2设备与BLE设备通讯的流程。

权限需求:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

步骤:
1.获取蓝牙服务
BluetoothManager btManager= (BluetoothManager) this.getSystemService(Context.BLUETOOTH_SERVICE);
2.获取蓝牙设备管理器(适配器)
BluetoothAdapter mBluetoothAdapter = btManager.getAdapter();
3.判断设备是否支持BLE蓝牙通讯,支持则判断蓝牙是否已开启,未开启蓝牙的设置开启。
if (mBluetoothAdapter != null && (!mBluetoothAdapter.isEnabled())){
mBluetoothAdapter.enable();//打开蓝牙
}
4.扫描当前附近的蓝牙BLE设备(这个操作比较耗电,要适时地停止)
5.停止设备扫描
6.与指定BLE设备建立Gatt连接
7.在连接成功后,设置扫描BLE设备支持的service和characteristic,并设置。这里需要注意,需要开启通知的,需要设置相应描述符。
8.读写操作
9.断开Gatt连接
10.关闭Gatt连接

在开发过程中,遇到的主要问题是onCharacteristicWrite失败回调,这个可能跟低功耗蓝牙外设有关,需要断开并关闭Gatt连接重新建立

下面是主要参考代码

package com.rollup.bluetoothlock;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID; import com.android.peephole.MyApplication;
import com.rollup.bluetoothlock.MyLog;
import com.rollup.bluetoothlock.Utils;
import com.rollup.bluetoothlock.Value; import android.R.integer;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothAdapter.LeScanCallback;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Handler;
import android.os.Message;
import android.widget.EditText;
import android.widget.Toast; public class BleManager {
private static final String TAG = "BleManager";
public static final int ERROR_NO_ERROR = 0;//成功
public static final int ERROR_BTDEVICE_EXC_FAIL = 1;// 蓝牙锁端执行错误
public static final int ERROR_PSD_ERROR = 2; // 钥匙错误
public static final int ERROR_CONNECT_FAIL = 3;
public static final int ERROR_DEFAULT = 4;
public static final int ERROR_NOT_FOUND_DEVICE = 5;// 未扫描到指定编码的锁
public static final int ERROR_HAVE_CONNECTED = 6;//上次连接未执行完毕
public static final int ERROR_NO_DEVICE = 7;//设备没有初始化
public static final int ERROR_CREATE_FAIL = 8; private static final int STOP_LESCAN = 1;
private final int OVER_TIMER = 60 * 1000;// 扫描超时时间 private Context mContext = null;
private BluetoothAdapter mBluetoothAdapter = null;
private BluetoothManager mBluetoothManager = null;
public BluetoothDevice mBluetoothDevice = null;
private BluetoothGatt mBluetoothGatt;
private static boolean isScanning = false;
private BluetoothGattCharacteristic writeCharacteristic = null;
private static boolean isConnectToDevice = false;
private String btName = null;
private String psd = null;
private boolean hasExcuteOpenDoor = false;// 开门命令已执行
private byte[] devceKey; private static boolean haveRetry = false;
private static BleManager instance = null;
private MyHandler mHandler = null; public static long lastOpendoorTime = 0; private OnBleScanOpenDoorCallback openDoorCallback = null; public interface OnBleScanOpenDoorCallback {
public void openDoorSuc();
public void openDoorFail(int error);
} private BleManager(Context context, String btName, String psd) {
this.mContext = context;
initBluetooth();
if (mBluetoothManager == null) {
MyLog.e(TAG, "Unable to initialize BluetoothManager.");
return;
}
this.btName = btName;
this.psd = psd;
devceKey = new byte[16];
} public static BleManager getInstance(String btName, String psd) {
if (instance == null) {
instance = new BleManager(MyApplication.getMyApplication(), btName,
psd);
} else {
instance.setBTinfo(btName, psd);
}
return instance;
} private void setBTinfo(String name, String psd) {
this.btName = name;
this.psd = psd;
} private void initBluetooth() {
mHandler = new MyHandler(this);
mBluetoothManager = (BluetoothManager) mContext
.getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager != null) {
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter != null) {
if (!mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.enable(); // 打开蓝牙
}
}
}
} /**
* 是否空闲可用
* @return
*/
public static boolean isFree(){
return (!isConnectToDevice)&&(!isScanning);
} public void startLeScan(boolean autoConnect) {
if (mBluetoothAdapter == null) {
return;
}
if (btName == null || btName.length() == 0) {
return;
} if (isScanning) {
return;
}
isScanning = true; if (autoConnect) {
mBluetoothAdapter.startLeScan(mLeScanCallback2);
} else {
mBluetoothAdapter.startLeScan(mLeScanCallback); // 此mLeScanCallback为回调函数
} mHandler.sendEmptyMessageDelayed(STOP_LESCAN, OVER_TIMER); // 这个搜索30秒,如果搜索不到则停止搜索
} private void stopLeScan() {
if (mHandler != null && mHandler.hasMessages(STOP_LESCAN)) {
mHandler.removeMessages(STOP_LESCAN);
}
if (mBluetoothAdapter == null) {
return;
} if (!isScanning) {
return;
}
mBluetoothAdapter.stopLeScan(mLeScanCallback);
mBluetoothAdapter.stopLeScan(mLeScanCallback2);
isScanning = false;
} private LeScanCallback mLeScanCallback = new LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int arg1, byte[] arg2) {
MyLog.i(TAG, "onLeScan() DeviceName------>" + device.getName()); // 在这里可通过device这个对象来获取到搜索到的ble设备名称和一些相关信息
if (device.getAddress() != null) {
if (device.getName().toLowerCase().replace("_", "")
.contentEquals(btName.toLowerCase().replace(" ", ""))) {
mBluetoothDevice = device;
stopLeScan();
}
}
}
}; private LeScanCallback mLeScanCallback2 = new LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int arg1, byte[] arg2) {
MyLog.i(TAG, "onLeScan() DeviceName------>" + device.getName()); // 在这里可通过device这个对象来获取到搜索到的ble设备名称和一些相关信息
if (device.getAddress() != null) {
if (device.getName().toLowerCase().replace("_", "")
.contentEquals(btName.toLowerCase().replace(" ", ""))) {
mBluetoothDevice = device;
stopLeScan();
connect();
}
}
}
}; private static class MyHandler extends Handler {
WeakReference<BleManager> wf = null; public MyHandler(BleManager manager) {
// TODO Auto-generated constructor stub
wf = new WeakReference<BleManager>(manager);
} @Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
switch (msg.what) {
case STOP_LESCAN:
MyLog.i(TAG, "MyHandler handleMessage STOP_LESCAN");
if (wf.get()!=null){
wf.get().stopLeScan();
}
MyLog.i(TAG, "release(), Scan time is up");
break;
}
}
} public int connect() {
if (mBluetoothDevice == null) {
MyLog.i(TAG, "BluetoothDevice is null.");
return ERROR_NO_DEVICE;
} if(isConnectToDevice){
MyLog.i(TAG, "Have connected to device");
return ERROR_HAVE_CONNECTED;
}
isConnectToDevice = true;
hasExcuteOpenDoor = false; // 两个设备通过BLE通信,首先需要建立GATT连接。这里我们讲的是Android设备作为client端,连接GATT Server mBluetoothGatt = mBluetoothDevice.connectGatt(mContext, false,
mGattCallback); // mGattCallback为回调接口 if (mBluetoothGatt != null) { if (mBluetoothGatt.connect()) {
MyLog.d(TAG, "Connect succeed.");
return ERROR_NO_ERROR;
} else {
MyLog.d(TAG, "Connect fail.");
return ERROR_CONNECT_FAIL;
}
} else {
MyLog.d(TAG, "BluetoothGatt null.");
return ERROR_CREATE_FAIL;
}
} private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
isConnectToDevice = true;
gatt.discoverServices(); // 执行到这里其实蓝牙已经连接成功了
MyLog.i(TAG, "Connected to GATT server.");
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
if (!hasExcuteOpenDoor) {
disconnect();
retryUnlock();
if (!haveRetry) {
//已失败重试过
if (openDoorCallback != null) {
openDoorCallback.openDoorFail(ERROR_CONNECT_FAIL);
}
}
} else {
MyLog.i(TAG, "BluetoothProfile.STATE_DISCONNECTED hasExcuteOpenDoor=TRUE");
haveRetry = false;
disconnect();
}
MyLog.i(TAG, "BluetoothProfile.STATE_DISCONNECTED --release()");
}
} public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
MyLog.i(TAG, "onServicesDiscovered");
List<BluetoothGattService> services = gatt.getServices();
for (int i = 0; i < services.size(); i++) {
MyLog.i(TAG, "[Service " + i + "] uuid:"
+ services.get(i).getUuid());
if (services.get(i).getUuid().equals(Value.uuid)) {
List<BluetoothGattCharacteristic> characteristics = services
.get(i).getCharacteristics();
for (int j = 0; j < characteristics.size(); j++) {
MyLog.i(TAG, "[Characteristic]"
+ characteristics.get(j).getUuid());
if (characteristics.get(j).getUuid()
.equals(Value.CHARACTERISTIC_READ)) {
boolean res = mBluetoothGatt
.setCharacteristicNotification(
characteristics.get(j), true);
// 打开通知描述
for (BluetoothGattDescriptor descriptor : characteristics
.get(j).getDescriptors()) {
descriptor
.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
MyLog.i(TAG,
"Characteristic set notification is Success!");
}
MyLog.i(TAG,
"onServicesDiscovered setCharacteristicNotification result="
+ res);
break;
}
}
characteristics = null;
}
} for (int i = 0; i < services.size(); i++) {
MyLog.i(TAG, "[Service " + i + "] uuid:"
+ services.get(i).getUuid());
if (services.get(i).getUuid().equals(Value.uuid)) {
List<BluetoothGattCharacteristic> characteristics = services
.get(i).getCharacteristics();
for (int j = 0; j < characteristics.size(); j++) {
MyLog.i(TAG, "[Characteristic]"
+ characteristics.get(j).getUuid());
if (characteristics.get(j).getUuid()
.equals(Value.CHARACTERISTIC_WRITE)) {
writeCharacteristic = characteristics.get(j);
final int charaProp = characteristics.get(j)
.getProperties();
// 如果该char可写
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
byte[] value = new byte[] { 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00 };
characteristics.get(j).setValue(value);
writeCharacteristic
.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
mBluetoothGatt
.writeCharacteristic(characteristics
.get(j));
value = null;
}
break;
}
}
characteristics = null;
}
}
services = null;
} else {
MyLog.i(TAG, "onServicesDiscovered status------>" + status);
}
} @Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
MyLog.i(TAG,
"onCharacteristicRead------>"
+ Utils.bytesToHexString(characteristic.getValue())); } @Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
MyLog.i(TAG,
"onCharacteristicChanged------>"
+ Utils.bytesToHexString(characteristic.getValue()));
MyLog.i(TAG, "UUID------>" + characteristic.getUuid().toString()); if (Value.CHARACTERISTIC_READ.equals(characteristic.getUuid())) {
byte[] value = characteristic.getValue();
if ((value[0] & 0xFF) == 0x81) {
for (int i = 0; i < 16; i++) {
devceKey[i] = value[i + 1];
}
// MyLog.i(TAG, "加密秘钥为:"+Utils.bytesToHexString(devceKey));
if (writeCharacteristic != null) {
byte[] cmd = new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00 };
if (psd.length() == 8) {
int j = 0;
for (int i = 0; i < 4; i++) {
cmd[5 + i] = (byte) (((((Integer.valueOf(psd
.charAt(j)) - 48) << 4) & (0xF0)) + ((Integer
.valueOf(psd.charAt(j + 1)) - 48) & 0x0F)) & 0xFF);
MyLog.i(TAG, "cmd" + (9 + i) + "=" + cmd[9 + i]);
j = j + 2;
}
}
byte[] cmd_encrypt = Utils.enCode(devceKey, cmd);
if (cmd_encrypt != null) {
byte[] request = new byte[17];
request[0] = 0x03;
for (int i = 1; i < (cmd_encrypt.length < request.length ? cmd_encrypt.length
: request.length); i++) {
request[i] = cmd_encrypt[i - 1];
}
writeCharacteristic.setValue(request);
writeCharacteristic
.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
// MyLog.i(TAG,
// "发送开锁命令:"+Utils.bytesToHexString(request));
mBluetoothGatt
.writeCharacteristic(writeCharacteristic);
request = null;
}
cmd_encrypt = null;
cmd = null;
} } else if ((value[0] & 0xFF) == 0x83) {
byte[] res = new byte[16];
for (int i = 0; i < 16; i++) {
res[i] = value[i + 1];
}
// MyLog.i(TAG, "解密秘钥为:"+Utils.bytesToHexString(devceKey));
// MyLog.i(TAG, "待解密数据为:"+Utils.bytesToHexString(res));
byte[] decode_res = Utils.deCode(devceKey, res);
hasExcuteOpenDoor = true;
if (decode_res != null) {
MyLog.i(TAG,
"获取到蓝牙开锁返回数据:"
+ Utils.bytesToHexString(decode_res));
if ((decode_res[0] & 0xFF) == 0x01) {
if ((decode_res[1] & 0xFF) == 0x00) {
MyLog.i(TAG, "开锁成功"); if (openDoorCallback != null) {
openDoorCallback.openDoorSuc();
}
lastOpendoorTime = System.currentTimeMillis();
} else if ((decode_res[1] & 0xFF) == 0x01) {
MyLog.i(TAG, "开锁失败:命令执行失败");
if (openDoorCallback != null) {
openDoorCallback
.openDoorFail(ERROR_BTDEVICE_EXC_FAIL);
}
} else if ((decode_res[1] & 0xFF) == 0x02) {
MyLog.i(TAG, "开锁失败:密钥解密失败,无效用户密钥");
if (openDoorCallback != null) {
openDoorCallback
.openDoorFail(ERROR_PSD_ERROR);
}
} else {
MyLog.e(TAG, "开锁返回异常");
if (openDoorCallback != null) {
openDoorCallback
.openDoorFail(ERROR_DEFAULT);
}
} new Timer().schedule(new TimerTask() { @Override
public void run() {
// TODO Auto-generated method stub
sendEndCmd();
}
}, 500); } else {
MyLog.e(TAG, "开锁返回动作标志位异常,此时锁还会再次返回其他数据");
if (openDoorCallback != null) {
openDoorCallback.openDoorFail(ERROR_DEFAULT);
}
} } else {
MyLog.e(TAG, "蓝牙开锁数据获取失败");
if (openDoorCallback != null) {
openDoorCallback.openDoorFail(ERROR_DEFAULT);
}
// disconnect(true);
}
decode_res = null;
res = null;
} else {
MyLog.e(TAG, " value[0] = " + value[0]
+ " ===release() 11111111");
// disconnect(true);
}
value = null;
}
} // 接受Characteristic被写的通知,收到蓝牙模块的数据后会触发onCharacteristicWrite
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
MyLog.i(TAG,
"onCharacteristicWrite status = " + status
+ ",onCharacteristicWrite------>"
+ Utils.bytesToHexString(characteristic.getValue()));
if (status != 0) {
// disconnect(true);
}
}
}; private void sendEndCmd() {
if (writeCharacteristic != null) {
byte[] cmd = new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00 };
byte[] cmd_encrypt = Utils.enCode(devceKey, cmd);
if (cmd_encrypt != null) {
byte[] request = new byte[17];
request[0] = 0x08;
for (int i = 1; i < (cmd_encrypt.length < request.length ? cmd_encrypt.length
: request.length); i++) {
request[i] = cmd_encrypt[i - 1];
}
writeCharacteristic.setValue(request);
writeCharacteristic
.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
mBluetoothGatt.writeCharacteristic(writeCharacteristic);
request = null;
}
cmd = null;
cmd_encrypt = null;
}
// release();
} private void retryUnlock() {
if (!haveRetry) {
haveRetry = true;
connect();
} else {
haveRetry = false;
MyLog.i(TAG, "retryUnlock haveRetry");
}
} public void setOpenDoorCallback(OnBleScanOpenDoorCallback callback) {
openDoorCallback = callback;
} public synchronized void disconnect() {
MyLog.i(TAG, "disconnect");
if (mBluetoothGatt != null) {
mBluetoothGatt.disconnect();
mBluetoothGatt.close();
mBluetoothGatt = null;
}
isConnectToDevice = false;
}
}
package com.example.common;

import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec; public class Utils { /** Convert byte[] to hex string.这里我们可以将byte转换成int,然后利用Integer.toHexString(int)来转换成16进制字符串。
* @param src byte[] data
* @return hex string
*/
public static String bytesToHexString(byte[] src){
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
/**
* Convert hex string to byte[]
* @param hexString the hex string
* @return byte[]
*/
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
} /**
* Convert char to byte
* @param c char
* @return byte
*/
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
} //将指定byte数组以16进制的形式打印到控制台
public static void printHexString( byte[] b) {
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
System.out.print(hex.toUpperCase() );
} } /**
* 真正的加密过程
* 1.通过密钥得到一个密钥专用的对象SecretKeySpec
* 2.Cipher 加密算法,加密模式和填充方式三部分或指定加密算 (可以只用写算法然后用默认的其他方式)Cipher.getInstance("AES");
* @param key
* @param src
* @return
* @throws Exception
*/
public static byte[] enCode(byte[] key, byte[] src) {
try {
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/ZeroBytePadding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(src);
return encrypted;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return null;
} } /**
* 解密
* @param view
*/
public static byte[] deCode(byte[] key, byte[] src){
try {
//获取AESkey
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/ZeroBytePadding"); //AES/ECB/ZeroBytePadding
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(src);
byte[] result;
if (decrypted.length<16){
result = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
for (int i=0; i<decrypted.length; i++){
result[i]=decrypted[i];
}
}else{
result = decrypted;
}
return result;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return null;
}
}
}
package com.example.common;

import java.util.UUID;

public class Value {
public static final String BLE_LOCK_SP_NAME = "BleLockConfig";
public static final String KEY_BLE_LOCK_ID = "kEY_BLE_LOCK_ID";
public static final String KEY_BLE_LOCK_KEY = "KEY_BLE_LOCK_KEY"; public final static UUID uuid = UUID.fromString("0000ff12-0000-1000-8000-00805f9b34fb");
public final static UUID CHARACTERISTIC_WRITE = UUID.fromString("0000ff01-0000-1000-8000-00805f9b34fb");//主机BLE发送给BLE模块数据使用
public final static UUID CHARACTERISTIC_READ = UUID.fromString("0000ff04-0000-1000-8000-00805f9b34fb");//BLE模块发送给主机BLE数据使用,NOTIFY功能发送,前提是双方连接上后,需要主机BLE主动打开BLE模块的UUID=0XFF04(NOTIFY)功能。
}
package com.example.bluetoothdemo;

import com.example.common.BleManager;
import com.example.common.Utils;
import com.example.common.BleManager.OnBleScanOpenDoorCallback; import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.Toast; public class MainActivity extends Activity implements OnClickListener, OnBleScanOpenDoorCallback{ private final String TAG = "MainActivity";
private final String KEY_BLE_LOCK_ID = "kEY_BLE_LOCK_ID";
private final String KEY_BLE_LOCK_KEY = "KEY_BLE_LOCK_KEY"; private BleManager mBleManager = null; private RelativeLayout relativeLayout = null;
private EditText idEditText = null;
private EditText psdEditText = null;
private Button scanButton = null;
private Button connectButton = null;
private ProgressDialog progressDialog = null; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main); relativeLayout = (RelativeLayout) findViewById(R.id.relativelayout);
scanButton = (Button) findViewById(R.id.button_scan);
connectButton = (Button) findViewById(R.id.button_connect);
idEditText = (EditText) findViewById(R.id.edittext_lock_id);
psdEditText = (EditText) findViewById(R.id.edittext_psd);
progressDialog = new ProgressDialog(this);
progressDialog.setMessage(getString(R.string.waiting)); SharedPreferences sp = getPreferences(MODE_PRIVATE);
String bleName = sp.getString(KEY_BLE_LOCK_ID, "ZY121711240013");
String blePsd = sp.getString(KEY_BLE_LOCK_KEY, "12345678");
idEditText.setText(bleName);
psdEditText.setText(blePsd); relativeLayout.setOnClickListener(this);
scanButton.setOnClickListener(this);
connectButton.setOnClickListener(this); mBleManager = BleManager.getInstance(bleName, blePsd); //这个this是一个上下文,只要在上面的BleManager工具类定义一个有参数就好
mBleManager.setOpenDoorCallback(this);
} @Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
} @Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
SharedPreferences sp = getPreferences(MODE_PRIVATE);
Editor editable = sp.edit();
editable.putString(KEY_BLE_LOCK_ID, idEditText.getText().toString().trim());
editable.putString(KEY_BLE_LOCK_KEY, psdEditText.getText().toString().trim());
editable.commit();
} @Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.button_scan:
break;
case R.id.relativelayout:
InputMethodManager m = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
m .hideSoftInputFromWindow(relativeLayout.getWindowToken(), 0);//比如EditView
break;
case R.id.button_connect:
progressDialog.show();
int res = mBleManager.connect(); if (res == BleManager.ERROR_NO_ERROR){
}else if (res == BleManager.ERROR_HAVE_CONNECTED){
//已经连接不能再次连
progressDialog.dismiss();
}else{
mBleManager.startLeScan(true);
} break; default:
break;
}
} @Override
protected void onDestroy() {
// TODO Auto-generated method stub if (mBleManager != null){
mBleManager.setOpenDoorCallback(null);
mBleManager.disconnect();
mBleManager = null;
}
super.onDestroy();
} @Override
public void openDoorSuc() {
// TODO Auto-generated method stub
runOnUiThread(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
if (progressDialog.isShowing()){
progressDialog.dismiss();
}
Toast.makeText(MainActivity.this, getString(R.string.open_door_suc), Toast.LENGTH_LONG).show();
}
}); } @Override
public void openDoorFail(final int error) {
// TODO Auto-generated method stub
runOnUiThread(new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
if (progressDialog.isShowing()){
progressDialog.dismiss();
}
int resId = R.string.error_default;
if (error==BleManager.ERROR_BTDEVICE_EXC_FAIL){
resId = R.string.error_btdevice_exc_fail;
}else if (error == BleManager.ERROR_CONNECT_FAIL){
resId = R.string.error_not_connect;
}else if (error == BleManager.ERROR_PSD_ERROR){
resId = R.string.error_psd_error;
}else if (error == BleManager.ERROR_NOT_FOUND_DEVICE){
resId = R.string.error_device_not_found;
}
Toast.makeText(MainActivity.this, resId, Toast.LENGTH_LONG).show();
}
}); }
}

Android 4.4.2上与BLE 蓝牙锁设备的通讯的更多相关文章

  1. Android开发之Ubuntu上Eclipse不显示手机设备

    一.准备工作   A.开启Android设备,用USB数据线连接到Ubuntu系统.   B.启用设备的USB调试模试    C.启动Eclipse,在Devices栏会现一个有很多???号的不明设备 ...

  2. Android ble 蓝牙4.0 总结

    本文介绍Android ble 蓝牙4.0,也就是说API level >= 18,且支持蓝牙4.0的手机才可以使用,如果手机系统版本API level < 18,也是用不了蓝牙4.0的哦 ...

  3. android ble蓝牙开发略解

    Android 蓝牙4.0开发 1.  权限和相关属性 “android:required="true"表示apk只有在具有bluetooth_le属性的系统里运行,这个4.3之前 ...

  4. Android ble 蓝牙4.0 总结一

    本文介绍Android ble 蓝牙4.0,也就是说API level >= 18,且支持蓝牙4.0的手机才可以使用,如果手机系统版本API level < 18,也是用不了蓝牙4.0的哦 ...

  5. Android ble蓝牙问题

    (1)蓝牙回调安卓4.4的蓝牙回调是在异步线程中(不在主线程),若要在蓝牙回调中执行更新界面的操作,记得切换到主线程去操作 (2)三星手机兼容性问题connectGatt()方法在某些三星手机上只能在 ...

  6. Android BLE蓝牙详细解读

    代码地址如下:http://www.demodashi.com/demo/15062.html 随着物联网时代的到来,越来越多的智能硬件设备开始流行起来,比如智能手环.心率检测仪.以及各式各样的智能家 ...

  7. Android -BLE蓝牙小DEMO

    代码地址如下:http://www.demodashi.com/demo/13890.html 原文地址: https://blog.csdn.net/vnanyesheshou/article/de ...

  8. Android BLE 蓝牙开发——扫码枪基于BLESSED

    一.蓝牙模式HID与BLE 当扫码枪与手机连接时,通常采用的是蓝牙HID(Human Interface Device)模式.本质上是一个把扫码枪作为一个硬件键盘,按照键盘协议把扫码后的结果逐个输入到 ...

  9. Android5.0(Lollipop) BLE蓝牙4.0+浅析demo连接(三)

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

随机推荐

  1. [BUG]Dreamweaver6做网页的一个图片文字不清晰的问题

    自己用Dreamweaver6做一个网页,使用PS做图片,为了节约下载流量,我把图片裁剪为GIF格式,通过系统自带的图片浏览器和美图看看,图片上的文字都是清晰的. 我把图片加载进入DW中后,在DW界面 ...

  2. post和get提交服务器编码过程

    参考资料:http://blog.csdn.net/z55887/article/details/46975679 先说出一个知识点: 如果浏览器端编码是UTF-8,那在服务器端解决乱码问题的方法有两 ...

  3. sdut1642Simple Arithmetics(模拟)

    链接 发个长长的模拟 这题要注意的地方挺多 -的个数 以及对齐的情况 全都注意好了 大数的加减乘就可以了 #include <iostream> #include<cstdio> ...

  4. Anaconda(miniconda)安装及使用--转

    https://www.waitalone.cn/anaconda-install-error.html         3,224   1.Anaconda概述 Anaconda是一个用于科学计算的 ...

  5. 针对谷歌默认最小字体12px的正确解决方案

    利用css3的缩放,其最终大小就是:12px * 0.9(缩放比例) = 10.8px; 居然行得通.但回头一想,这么写的话,IE7 IE8会不会不兼容,还是12px呢?不出所料,果然不兼容.此时,又 ...

  6. 让px单位自动转换为rem的方法

    开发工具:      编辑器:vscode;     css预处理器:less;(无具体要求): 步骤:   1. vscode安装cssrem插件:   2. 修改css插件的默认配置,其默认转换p ...

  7. redis 在windows 集群

    前言:为什么自己要花时间写一篇redis集群文章,网上众多的文章大都是思路正确,但是细节不足,这里写一篇文章记录自己部署时候遇到的问题,当下次再部署的时候避免跳入重复的坑. 上篇文章(http://w ...

  8. mac 下使用gcc 编译c函数

    mac 终端其实和window 的cmd类似,由于mac 的os x 采用了unix 系统,所以,各种类似UNIX下的命令都有用.最近在看computer science ,用到了命令行. 下面是一个 ...

  9. [翻译] API测试最佳实践 - 组织你的测试

    组织你的测试 适用级别:初学者 在最底层,一个测试步骤(Test Step)用来验证一个单独的操作.组合若干测试步骤到测试用例,允许你验证那些被分隔出来的一个一个的功能,这些功能是应用程序所需要的.接 ...

  10. shellinabox的安装使用

    一.简介 Shell In A Box(发音是shellinabox)是一款基于Web的终端模仿器,由Markus Gutschke开辟而成.它有内置的Web办事器,在指定的端口上作为一个基于Web的 ...