編輯:關於Android編程
BLE 概述 :
-- 版本支持 :Android 4.3 (API Level 18) 內置框架引入了 藍牙低功耗方案 (Bluetooth Low Energy, BLE) 支持;
-- 角色支持 : Android 手機只能作為 主設備 (central role), 開發者開發的 APP 可以使用其提供的 API 接口, 用於 發現設備, 遍歷服務 (services), 讀寫服務中的特性 (characteristics).
-- 傳統藍牙對比 : 與傳統的藍牙對比, 藍牙低功耗方案 (Bluetooth Low Energy) 是出於更低的電量消耗考慮而設計的. 這可以使 Android 應用可以與 BLE 設備進行交流, 這些設備需要很低的電量, 如 近距離傳感器, 心率測量設備, 健康設備 等等.
Generic Attribute Profile (GATT) 通用屬性規范:
-- GATT 作用 : GATT 規范是一個針對 在 BLE 連接上的, 發送 和 接收 少量數據的一個規范, 所有的現有的低功耗應用的規范都是基於這個 GATT 規范制定的.
-- 制定者 : 藍牙技術聯盟 (Bluetooth SIG) 為低功耗設備定義了許多規范, 一個 規范 (Profile) 就是 設備如何在特定的應用中工作的詳述.
-- 設備規范對應關系 : 此外, 一個設備可以實現多個規范, 如 : 一個設備可以包含一個心率檢測器, 和 電量檢測器.
Attribute Protocol (ATT) 屬性協議 :
-- ATT 與 GATT 關系 : GATT 規范是建立在 ATT 的上一層的, 這套改改通常被稱為 GATT/ATT.
-- ATT 作用 : ATT 被用於優化 BLE 設備的運行, 為了這個目的, ATT (屬性協議) 使用盡可能少的字節.
-- ATT 唯一標識 : ATT 中的每個屬性都被 一個 UUID (Universally Unique Identifier) 獨一無二的進行標識, UUID 是一個 128 比特的標准的字符串 ID, 用於信息的唯一標識.
-- ATT 屬性 : ATT 中定義的屬性就是 Charicteristics (特性) 和 Services (服務);
Characteristic 特性 :
-- Characteristic 概念 : 一個 Characteristic 特性包含了一個值 和 多個 Descriptor (描述符) 用於描述這個特性的值.
-- 本質 : 一個特性可以被認為是一個類型, 類似於一個類.
Descriptor 描述符 :
-- 作用 : 描述符 被定義為一些屬性, 這些屬性用於描述 Characteristic (特性) 的值.
-- 示例 : 例如, 一個 描述符 可以說明一個 可讀的描述, 一個 特性值的可接受范圍, 或者 一個特性值的測量單元.
Service 服務 :
-- 服務本質 : 服務是 Characteristic (特性) 的集合.
-- 示例 : 如, 你可以有一個 名稱為 "Heart RateMonitor (心率監控)" 的服務, 包含了特性 "Heart Rate Measurement (心率測量)".
-- 參考資料 : 你可以在 bluetooth.org 官網查詢到一個基於 GATT 服務 和 規范的列表.
Android 設備 與 BLE 設備互動時, 設備的角色 和 職責 :
-- 中心設備 和 外圍設備 : 這個角色體系適用於 BLE 連接. 中心設備角色 可以掃描, 查找廣播. 外圍設備角色 發送廣播.
-- GATT 服務器 和 GATT 客戶端 : 這個決定了兩個設備之間, 一旦建議連接後, 如何進行互相通信.
BLE 連接需要兩種設備都存在 : 為了理解其中的區別, 想象一下 你有一個 Android 設備 和 一個激活的 智能腕表藍牙設備. 手機支持作為 中心設備 角色, 智能腕表 藍牙設備支持作為外圍設備角色, 為了建立 BLE 連接, 只有外圍設備 或者 只有 中心設備 都不能建立 BLE連接.
GATT 服務器 和 GATT 客戶端 簡介 :
-- GATT 服務器 和 GATT 客戶端 角色不是固定的 : 一旦手機 和 智能腕表 設備建立了 BLE 連接, 它們開始互相交換 GATT 元數據. 根據它們之間傳輸的數據類型, 其中的一個會扮演 GATT 服務器的角色.
-- 角色改變示例 : 如果 智能腕表 設備想要向手機報告傳感器數據, 那麼智能腕表必須當做 GATT 服務器. 如果智能腕表 想要從手機上接受更新數據, 那麼 Android 手機就是 GATT 服務器.
-- 手機 和 設備 都可以作為 GATT 服務器 和 客戶端 : 在本文檔中使用的示例代碼, 在 Android 設備上運行的 Android APP 就是 GATT 客戶端, BLE 外圍設備就是 GATT 服務器. Android APP 從 GATT 服務器上獲取數據, 服務器的 BLE "heart rate monitor (心率監測)" 支持 "Heart Rate Profile (心率規范 - 一種 BLE 藍牙標准規范)". Android APP 也可以作為 GATT 服務器;
Android 藍牙權限簡介 :
-- 權限作用 : 為了在應用中使用藍牙功能, 必須在 AndroidManifest.xml中 聲明藍牙權限. 所有的藍牙通信操作都需要 藍牙權限 來允許執行, 例如 搜索藍牙, 藍牙連接, 數據交互等操作.
-- 搜索設置藍牙權限 : 如果 APP 要發起設備搜索 或者 管理 藍牙設置, 需要 提前聲明 BLUETOOTH_ADMIN 權限.
-- 注意 : 使用 BLUETOOTH_ADMIN 權限的前提是 必須聲明 BLUETOOTH 權限.
藍牙權限示例 :
-- AndroidManifest.xml 聲明藍牙權限示例 :
動態控制 BLE 是否可用 : 不管怎樣, 如果你想要讓你的 APP 可以當做 BLE 設備, 但是手機不支持這個操作, 你仍然可以進行如下配置, 只是將其中的 android:required 設置成 false. 此時在運行時, 你可以使用 "PackageManager.hasSystemFeature()" 方法決定 BLE 是否可用.
//使用下面的函數決定 設備上的 BLE 功能 是否可用 //此時你可以選擇性的關閉 BLE 相關的功能 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); finish(); }
創建 BLE 簡介 :
-- 驗證 BLE 功能 : 在應用可以通過 BLE 交互之前, 你需要驗證設備是否支持 BLE 功能, 如果支持, 確定它是可以使用的.
-- 注意 : 這個檢查只有在 下面的配置 設置為 false 時才是必須的;
-- 支持 BLE 打開藍牙 : 如果 BLE 支持 BLE 功能, 但是設備的藍牙是關閉的, 你可以在應用中請求打開設備的藍牙模塊.
-- 步驟總結 : 創建 BLE 藍牙的過程分成兩個步驟, 1. 獲取 BluetoothAdapter, 2. 打開 設備的藍牙模塊.
獲取 BluetoothAdapter 藍牙適配器 :
-- BluetoothAdapter 類作用 : 所有的藍牙活動都需要 BluetoothAdapter, BluetoothAdapter 代表了設備本身的藍牙適配器 (藍牙無線設備). 整個系統中只有一個 藍牙適配器, 應用可以使用 BluetoothAdapter 對象與 藍牙適配器硬件進行交互.
-- 獲取 BluetoothAdapter 代碼示例 :
// 初始化藍牙適配器 final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter();
-- 注意 : 這個方法使用了 getSystemService() 方法, 返回了一個 BluetoothManager 實例對象, 從 BluetoothManager 實例對象中可以獲取 BluetoothAdapter 對象;
打開藍牙 :
-- 檢查是否可用 : 為了保證 藍牙功能是打開的, 調用 BluetoothAdapter 的 isEnable() 方法, 檢查藍牙在當前是否可用. 如果返回 false, 說明當前藍牙不可用.
-- 示例代碼 :
private BluetoothAdapter mBluetoothAdapter; ... // 確認當前設備的藍牙是否可用, // 如果不可用, 彈出一個對話框, 請求打開設備的藍牙模塊 if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); }
查找 BLE 設備 :
-- 查找方法參數 : 為了搜索到BLE 設備, 調用 BluetoothAdapter 的 startLeScan() 方法, 該方法需要一個 BluetoothAdapter.LeScanCallback 類型的參數. 你必須實現這個 LeScanCallback 接口, 因為 BLE 藍牙設備掃描結果在這個接口中返回.
-- 查找策略 : 藍牙搜索是非常耗電的, 你需要遵守以下的 中斷策略 和 不循環策略.
-- 中斷策略 : 只要一發現藍牙設備, 馬上中斷掃描.
-- 不循環策略 : 不要循環掃描, 設置一個掃描的最大時間限制. 一個設備在之前可用, 繼續掃描可能會使設備不可用, 此外繼續掃描會持續浪費電池電量.
-- 源碼示例 :
/** * 搜索 和 展示 可用的藍牙設備 的 Activity 界面 */ public class DeviceScanActivity extends ListActivity { private BluetoothAdapter mBluetoothAdapter; private boolean mScanning; private Handler mHandler; // 10 秒後停止搜索 private static final long SCAN_PERIOD = 10000; ... private void scanLeDevice(final boolean enable) { if (enable) { // 在一個預先定義的時間段後停止掃描. 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); } ... } ... }
查找特定 BLE 設備 :
-- 方法調用 : 查找特定類型的外圍設備, 可以調用下面的方法, 這個方法需要提供一個 UUID 對象數組, 這個 UUID 數組是 APP 支持的 GATT 服務的特殊標識.
-- 示例 :
startLeScan(UUID[], BluetoothAdapter.LeScanCallback)
-- 接口作用 : BluetoothAdapter.LeScanCallback 實現類, 在這個實現類的接口中返回 BLE 設備掃描結果;
-- 源碼示例 :
private LeDeviceListAdapter mLeDeviceListAdapter; ... // 設備掃描回調接口 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 設備 或者 SPP 設備中的一種, 不能同時掃描兩種設備.
連接指定設備 :
-- 連接到 GATT 服務 : 與 BLE 設備交互的第一步是 連接到 BLE 設備中的 GATT 服務.
-- 實現方法 : 調用 BluetoothDevice 的 connectGatt() 方法可以連接到 BLE 設備的 GATT 服務.
-- 參數解析 : connectGatt() 方法需要三個參數, 參數一 Context 上下文對象, 參數二 boolean autoConnect 是否自動連接掃描到的藍牙設備, 參數三 BluetoothGattCallback 接口實現類.
-- 用法示例 :
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
-- 獲取 BluetoothGatt 對象 : 調用 connectGatt() 方法可以連接到 BLE 設備上的 GATT 服務, 返回一個 BluetoothGatt 實例對象, 你可以使用這個對象去 管理 GATT 客戶端操作.
-- GATT 客戶端操作 : Android APP 可以調用 GATT Client (客戶端). BluetoothGattCallback 可以用於傳遞結果到 GATT 客戶端, 如 連接狀態 和 更進一步的 GATT Client 操作.
BLE 藍牙數據交互 :
-- 界面 : 在下面的示例中, BLE 應用提供了一個 Activity 界面, 該 Activity 界面用於 連接, 展示數據, 展示 GATT 服務 和 設備支持的特性.
-- BLE 藍牙服務類 : 基於用戶的輸入, 這個 Activity 界面可以與一個 BluetoothLeService 的服務進行交流, 該交流的本質就是 BLE 設備的 GATT 服務 與 Android 的 BLE API 進行交流.
-- BLE 藍牙服務類 示例代碼 :
// BLE 設備可以通過該服務 與 Android 的 BLE API 進行互動 public class BluetoothLeService extends Service { private final static String TAG = BluetoothLeService.class.getSimpleName(); private BluetoothManager mBluetoothManager; private BluetoothAdapter mBluetoothAdapter; private String mBluetoothDeviceAddress; private BluetoothGatt mBluetoothGatt; private int mConnectionState = STATE_DISCONNECTED; private static final int STATE_DISCONNECTED = 0; private static final int STATE_CONNECTING = 1; private static final int STATE_CONNECTED = 2; public final static String ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; public final static String ACTION_GATT_DISCONNECTED = "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED"; public final static String ACTION_DATA_AVAILABLE = "com.example.bluetooth.le.ACTION_DATA_AVAILABLE"; public final static String EXTRA_DATA = "com.example.bluetooth.le.EXTRA_DATA"; public final static UUID UUID_HEART_RATE_MEASUREMENT = UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT); // BLE API 中定義的不同的回調方法. private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override // BLE 設備的狀態改變 連接 斷開 public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { String intentAction; if (newState == BluetoothProfile.STATE_CONNECTED) { intentAction = ACTION_GATT_CONNECTED; mConnectionState = STATE_CONNECTED; broadcastUpdate(intentAction); Log.i(TAG, "連接到了 GATT 服務."); Log.i(TAG, "嘗試搜索服務:" + mBluetoothGatt.discoverServices()); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { intentAction = ACTION_GATT_DISCONNECTED; mConnectionState = STATE_DISCONNECTED; Log.i(TAG, "於 GATT 服務斷開連接."); broadcastUpdate(intentAction); } } @Override // BLE 設備中 新的 GATT 服務被發現 public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); } else { Log.w(TAG, "發現 GATT 服務 : " + status); } } @Override // 特性讀取操作返回的數據 public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } } ... }; ... }
-- 廣播發送 : 當一個特定的回調被觸發, 它調用適當的broadcastUpdate() 幫助方法, 將其當做一個 Action 操作傳遞出去.
-- 注意藍牙心率: 這部分的數據解析 與 藍牙心率測量 是一起被執行的.
-- 廣播發送 示例代碼 :
private void broadcastUpdate(final String action) { final Intent intent = new Intent(action); sendBroadcast(intent); } private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) { final Intent intent = new Intent(action); // This is special handling for the Heart Rate Measurement profile. Data // parsing is carried out as per profile specifications. // 心率監測規范的特殊處理 // 數據解析在每個規范中完成 if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); int format = -1; if ((flag & 0x01) != 0) { format = BluetoothGattCharacteristic.FORMAT_UINT16; Log.d(TAG, "心率格式 UINT16."); } else { format = BluetoothGattCharacteristic.FORMAT_UINT8; Log.d(TAG, "心率格式 UINT8."); } final int heartRate = characteristic.getIntValue(format, 1); Log.d(TAG, String.format("接收到心跳檢測 : %d", heartRate)); intent.putExtra(EXTRA_DATA, String.valueOf(heartRate)); } else { // 對於其它的規范, 寫出 HEX 十六進制格式的數據 final byte[] data = characteristic.getValue(); if (data != null && data.length > 0) { final StringBuilder stringBuilder = new StringBuilder(data.length); for(byte byteChar : data) stringBuilder.append(String.format("%02X ", byteChar)); intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString()); } } sendBroadcast(intent); }
// 處理 Service 發起的的不同事件 // ACTION_GATT_CONNECTED: 連接到 GATT 服務. // ACTION_GATT_DISCONNECTED: 與 GATT 服務斷開. // ACTION_GATT_SERVICES_DISCOVERED: 發現 GATT 服務. // ACTION_DATA_AVAILABLE: 從 BLE 設備中接收數據, 數據可以是 read 或者 notification 操作的結果. private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { mConnected = true; updateConnectionState(R.string.connected); invalidateOptionsMenu(); } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { mConnected = false; updateConnectionState(R.string.disconnected); invalidateOptionsMenu(); clearUI(); } else if (BluetoothLeService. ACTION_GATT_SERVICES_DISCOVERED.equals(action)) { // 在用戶界面 顯示所有支持的服務 和 特性. displayGattServices(mBluetoothLeService.getSupportedGattServices()); } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) { displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA)); } } };
讀寫屬性簡介 :
-- 讀寫屬性前提 : Android 應用連接到了 設備中的GATT 服務, 並且發現了 各種服務 (特性集合), 可以讀寫其中的屬性.
-- 讀寫屬性代碼示例: 遍歷服務 (特性集合) 和 特性, 將其展示在 UI 界面中.
public class DeviceControlActivity extends Activity { ... // 示范如何通過其所支持的 GATT 遍歷 服務 (Services) 和 特性 (Characteristics) // 在這個示例中, 我們將查詢出的數據填充到 UI 界面中的 ExpandableListView 中 private void displayGattServices(ListgattServices) { if (gattServices == null) return; String uuid = null; String unknownServiceString = getResources(). getString(R.string.unknown_service); String unknownCharaString = getResources(). getString(R.string.unknown_characteristic); ArrayList > gattServiceData = new ArrayList >(); ArrayList>> gattCharacteristicData = new ArrayList>>(); mGattCharacteristics = new ArrayList>(); // 遍歷 GATT 服務 for (BluetoothGattService gattService : gattServices) { HashMap currentServiceData = new HashMap (); uuid = gattService.getUuid().toString(); currentServiceData.put( LIST_NAME, SampleGattAttributes. lookup(uuid, unknownServiceString)); currentServiceData.put(LIST_UUID, uuid); gattServiceData.add(currentServiceData); ArrayList > gattCharacteristicGroupData = new ArrayList >(); // 獲取服務中的特性集合 List gattCharacteristics = gattService.getCharacteristics(); ArrayList charas = new ArrayList (); // 循環遍歷特性集合 for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { charas.add(gattCharacteristic); HashMap currentCharaData = new HashMap (); uuid = gattCharacteristic.getUuid().toString(); currentCharaData.put( LIST_NAME, SampleGattAttributes.lookup(uuid, unknownCharaString)); currentCharaData.put(LIST_UUID, uuid); gattCharacteristicGroupData.add(currentCharaData); } mGattCharacteristics.add(charas); gattCharacteristicData.add(gattCharacteristicGroupData); } ... } ... }
GATT 通知簡介 :
-- 特性改變通知 : 當 BLE 設備中的一些特殊的特性改變, 需要通知與之連接的 Android BLE 應用.
-- 代碼示例 : 使用setCharacteristicNotification() 方法為特性設置通知.
private BluetoothGatt mBluetoothGatt; BluetoothGattCharacteristic characteristic; boolean enabled; ... // 設置是否監聽某個特性改變 mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); ... BluetoothGattDescriptor descriptor = characteristic.getDescriptor( UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG)); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor);
@Override // 特性通知 public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); }
關閉 BLE 設備連接 :
-- 關閉方法 : 一旦結束了 BLE 設備的使用, 調用 BluetoothGatt 的 close() 方法, 關閉 BLE 連接, 釋放相關的資源.
-- 關閉示例 :
public void close() { if (mBluetoothGatt == null) { return; } mBluetoothGatt.close(); mBluetoothGatt = null; }
轉載請注明出處:http://blog.csdn.net/shulianghan/article/details/50515359
五一放假,閒著沒事,裝了最新的ubutun16.04,然後打算順道把android開發環境移植到ubuntu上來體驗一下。 1.首先下載了Android stu
1. 簡介直到4g時代,流量依然是寶貴的東西。而移動網絡傳輸中,最占流量的一種載體:圖片,成為了我們移動開發者不得不關注的一個問題。我們關注的問題,無非是圖片體積和質量如
接著上一篇博客,上一篇博客跟大家分享了三種開始頁面的定時跳轉,根據項目需求接下來就說一下向導頁面吧!幾乎每一個APP都有自己的向導頁面,一般都是第一次安裝的時或者第一次進
由於手機屏幕的高度有限,所以如果面對組件要顯示多組信息的時候,ScrollView視圖(滾動視圖)可以有效的安排這些組件,浏覽時可以自動的進行滾屏的操作。 android