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(); } }); } }