編輯:關於Android編程
Google API:http://developer.android.com/guide/topics/connectivity/bluetooth-le.html
其實大家要學習Android的技術,Google的API就是最詳細的指導書了,而且通俗易懂,就算看不懂英語,翻譯翻譯再結合代碼也能看個大概的,真的很贊喲,我們直接解讀吧!
Google的API中前面就是一大片的概念,這其實在我之前的篇幅中都有說過的
Generic Attribute Profile(GATT)—GATT配置文件是一個通用規范,用於在BLE鏈路上發送和接收被稱為“屬性”的數據塊。目前所有的BLE應用都基於GATT。 藍牙SIG規定了許多低功耗設備的配置文件。配置文件是設備如何在特定的應用程序中工作的規格說明。注意一個設備可以實現多個配置文件。例如,一個設備可能包括心率監測儀和電量檢測。
Attribute Protocol(ATT)—GATT在ATT協議基礎上建立,也被稱為GATT/ATT。ATT對在BLE設備上運行進行了優化,為此,它使用了盡可能少的字節。每個屬性通過一個唯一的的統一標識符(UUID)來標識,每個String類型UUID使用128 bit標准格式。屬性通過ATT被格式化為characteristics和services。
Characteristic 一個characteristic包括一個單一變量和0-n個用來描述characteristic變量的descriptor,characteristic可以被認為是一個類型,類似於類。
Descriptor Descriptor用來描述characteristic變量的屬性。例如,一個descriptor可以規定一個可讀的描述,或者一個characteristic變量可接受的范圍,或者一個characteristic變量特定的測量單位。
Service service是characteristic的集合。例如,你可能有一個叫“Heart Rate Monitor(心率監測儀)”的service,它包括了很多characteristics,如“heart rate measurement(心率測量)”等。你可以在bluetooth.org 找到一個目前支持的基於GATT的配置文件和服務列表。
這裡有兩個概念
中央 VS 外圍設備。 適用於BLE連接本身。中央設備掃描,尋找廣播;外圍設備發出廣播。 GATT 服務端 VS GATT 客戶端。決定了兩個設備在建立連接後如何互相交流。好的,Google文檔中,也舉了一個例子說明,我們要使用BLE的時候,必須有要加上兩個權限
這其實你要使用藍牙這個硬件都是要加上這個權限的,但是這裡Google又聲明了一點
如果n你想聲明你的軟件只為具有BLE的設備提供服務的話,你應該要在清單文件中加入
如果改為false的話,那其他藍牙也是可以使用的,我們創建一個工程——BLETest
和傳統藍牙一樣,我們添加完權限之後就要去判斷這個設備是否支持BLE
//判斷是否支持BLE設備
if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "此設備不支持BLE", Toast.LENGTH_SHORT).show();
finish();
}
這步操作也只是你設置為false的時候才是必須的,因為你如果你設置為true,那你只給BLE服務,那這個判斷也就是多余的了,緊接著,我們還需要去判斷藍牙是否開啟,如果沒有開啟,我們就去開啟他,這次雖然也是用BluetoothAdapter 去獲取,但是這裡用了一個新的類BluetoothManager ,先初始化
//初始化藍牙適配器
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
然後再去開啟
//打開藍牙
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
這裡的REQUEST_ENABLE_BT也就是一個回調的標志,無須理會
public static final int REQUEST_ENABLE_BT = 0;
寫了這個之後,當我們藍牙沒有開啟的時候就會去友好的提示用戶開啟了
vcnosbjWrrrzvs3No9a5yajD6Lvy1d/J6NbDyajD6MqxvOSjrLK7yLvE477Nv+CxxsHLPC9wPg0KPHByZSBjbGFzcz0="brush:java;">
//搜索設備
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
private void scanLeDevice(final boolean enable) {
if (enable) {
// 經過預定掃描期後停止掃描
mHandler.postDelayed(new Runnable() {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
這樣,當我們接收到搜索設備的回調時便可以直接添加在Adapter上,要注意,所演示的也是Google提供的Demo
//搜索設備的回調
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擁有強大的能力,就需要連接他的GATT服務端
/**
* 1.上下文
* 2.自動連接
* 3.BluetoothGattCallback回調
*/
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
連接到GATT服務端時,由BLE設備做主機,並返回一個BluetoothGatt實例,然後你可以使用這個實例來進行GATT客戶端操作。請求方(軟件)是GATT客戶端。BluetoothGattCallback用於傳遞結果給用戶,例如連接狀態,以及任何進一步GATT客戶端操作。
在這個例子中,這個BLE APP提供了一個activity(DeviceControlActivity)來連接,顯示數據,顯示該設備支持的GATT services和characteristics。根據用戶的輸入,這個activity與BluetoothLeService通信,通過Android BLE API實現與BLE設備交互。
package com.lgl.bletest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.util.Log;
import java.util.UUID;
/**
* Created by LGL on 2016/5/13.
*/
public class BluetoothLeService extends BluetoothClass.Service {
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
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, "Connected to GATT server.");
Log.i(TAG, "Attempting to start service discovery:" +
mBluetoothGatt.discoverServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {//當設備無法連接
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
broadcastUpdate(intentAction);
}
}
@Override
// 發現新服務端
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
@Override
// 讀寫特性
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
};
}
當觸發特定的回調時,它調用適當的broadcastUpdate()輔助方法,通過這一個動作。注意,本節中的數據解析執行按照藍牙心率測量概要文件規范:
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, "Heart rate format UINT16.");
} else {
format = BluetoothGattCharacteristic.FORMAT_UINT8;
Log.d(TAG, "Heart rate format UINT8.");
}
final int heartRate = characteristic.getIntValue(format, 1);
Log.d(TAG, String.format("Received heart rate: %d", heartRate));
intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
} else {
// For all other profiles, writes the data formatted in 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);
}
返回DeviceControlActivity, 這些事件由一個BroadcastReceiver來處理:
// Handles various events fired by the Service.
// ACTION_GATT_CONNECTED: connected to a GATT server.
// ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
// ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
// ACTION_DATA_AVAILABLE: received data from the device. This can be a
// result of read or notification operations.
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)) {
// Show all the supported services and characteristics on the
// user interface.
displayGattServices(mBluetoothLeService.getSupportedGattServices());
} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
}
}
};
你的android app完成與GATT服務端連接和發現services後,就可以讀寫支持的屬性。例如,這段代碼通過服務端的services和 characteristics迭代,並且將它們顯示
在UI上。
public class DeviceControlActivity extends Activity {
...
// Demonstrates how to iterate through the supported GATT
// Services/Characteristics.
// In this sample, we populate the data structure that is bound to the
// ExpandableListView on the UI.
private void displayGattServices(List gattServices) {
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>();
// / 循環可用的Characteristics.
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();
// Loops through available Characteristics.
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);
}
...
}
...
}
當設備上的特性改變時會通知BLE應用程序。這段代碼顯示了如何使用setCharacteristicNotification( )給一個特性設置通知。
//GATT服務端
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);
如果對一個特性啟用通知,當遠程藍牙設備特性發送變化,回調函數onCharacteristicChanged( ))被觸發。
@Override
// 廣播更新
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
當你的app完成BLE設備的使用後,應該調用close( ),系統可以合理釋放占用資源。
public void close() {
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}
代碼可能有點亂,這裡提供一個Google的Demo功大家測試
Buzz桌面安裝好之後,是不會自動將已安裝的手機應用程序圖標添加到桌面上的,需要我們手動添加或拖動應用程序圖標到桌面,下面就讓我們來看看如何將已經安裝的應用
Android手勢密碼LockPatternView、LockPasswordUtils、LockPatternUtils在使用別人寫的這個手勢密碼的時候,我們通常是有自
閒談最近公司事情不算太多,閒來無事,看到項目中用到的廣告輪播圖,之前都是使用第三方的,趁事情不算多,所以自己實現一個廣告位輪播圖barner組件,這樣的話,在以後的開發中
最近在開發安卓的過程中發現ListView無疑是一個經常使用的組件,而提到ListView又不得不提一下適配器了,我打算用幾個實例,由淺入深的向大家仔細講解一下適配器的使