Android對外模模式(peripheral)的支持:java
從Android 5.0+開始才支持。 api level >= 21android
因此5.0 以前設備,是不能向外發送廣播的。ios
Android中心設備(central)的支持:git
從Android 4.3+ 。 api level >= 18github
一、初始化藍牙api
二、檢查ble是否可用數組
三、開啓廣播ide
四、掃描響應數據ui
五、建立iBeacon 廣播數據this
六、廣播設置
七、開啓廣播後的回調
(1)初始化藍牙:
添加權限:
1 <uses-permission android:name="android.permission.BLUETOOTH" /> 2 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> 3 <!-- 6.0以後藍牙還須要地理位置權限 --> 4 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> 5 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> 6 7 <!-- 自行判斷 --> 8 <uses-feature 9 android:name="android.hardware.bluetooth_le" 10 android:required="false" />
初始化:
1 //初始化BluetoothManager和BluetoothAdapter 2 if (mBluetoothManager == null) { 3 mBluetoothManager = (BluetoothManager) mContext.getSystemService(BLUETOOTH_SERVICE); 4 } 5 6 if (mBluetoothManager != null && mBluetoothAdapter == null) { 7 mBluetoothAdapter = mBluetoothManager.getAdapter(); 8 }
(2)檢查是否可以使用ble:
1 if (!activity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { 2 Toast.makeText(activity, "不支持ble", Toast.LENGTH_LONG).show(); 4 return; 5 } 6 7 final BluetoothManager mBluetoothManager = (BluetoothManager) activity.getSystemService(BLUETOOTH_SERVICE); 8 mBluetoothAdapter = mBluetoothManager.getAdapter(); 9 10 if (mBluetoothAdapter == null) { 11 Toast.makeText(activity, "不支持ble", Toast.LENGTH_LONG).show();13 return; 14 } 15 // 獲取藍牙ble廣播 16 mBluetoothAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser(); 17 if (mBluetoothAdvertiser == null) { 18 Toast.makeText(activity, "the device not support peripheral", Toast.LENGTH_SHORT).show(); 19 Log.e(TAG, "the device not support peripheral");21 return; 22 }
(3)開啓廣播:
public void startAdvertising(MockServerCallBack callBack) { //獲取BluetoothLeAdvertiser,BLE發送BLE廣播用的一個API if (mBluetoothAdvertiser == null) { mBluetoothAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser(); } if (mBluetoothAdvertiser != null) {try { //建立BLE beacon Advertising而且廣播 mBluetoothAdvertiser.startAdvertising(createAdvSettings(true, 0) , createIBeaconAdvertiseData(BluetoothUUID.bleServerUUID, mMajor, mMinor, mTxPower) , createScanAdvertiseData(mMajor, mMinor, mTxPower, mAdvCallback); } catch (Exception e) { Log.v(TAG, "Fail to setup BleService"); } } }
一個ble廣播包:廣播數據其實包含兩部分:Advertising Data(廣播數據) 和 Scan Response Data(掃描響應數據)。
一般狀況下,廣播的一方,按照必定的間隔,往空中廣播 Advertising Data。
當某個監聽設備監聽到這個廣播數據時候,會經過發送 Scan Response Request,請求廣播方發送掃描響應數據數據。
這兩部分數據的長度都是固定的 31 字節。
在 Android 中,系統會把這兩個數據拼接在一塊兒,返回一個 62 字節的數組。
(4)掃描響應數據:
能夠自定義數據,好比增長溼度,溫度等。
1 //設置scan廣播數據 2 public static AdvertiseData createScanAdvertiseData(short major, short minor, byte txPower) { 3 AdvertiseData.Builder builder = new AdvertiseData.Builder(); 4 builder.setIncludeDeviceName(true); 5 6 byte[] serverData = new byte[5]; 7 ByteBuffer bb = ByteBuffer.wrap(serverData); 8 bb.order(ByteOrder.BIG_ENDIAN); 9 bb.putShort(major); 10 bb.putShort(minor); 11 bb.put(txPower); 12 builder.addServiceData(ParcelUuid.fromString(BluetoothUUID.bleServerUUID.toString()) 13 , serverData); 14 15 AdvertiseData adv = builder.build(); 16 return adv; 17 }
五、建立ibeacon 廣播數據。
iBeacon 的廣播結構:iBeacon 只是協議.
Byte 0-2: Standard BLE Flags
Byte 0: Length : 0x02 Byte 1: Type: 0x01 (Flags) Byte 2: Value: 0x06 (Typical Flags)
Byte 3-29: Apple Defined iBeacon Data
Byte 3: Length: 0x1a Byte 4: Type: 0xff (Custom Manufacturer Packet) Byte 5-6: Manufacturer ID : 0x4c00 (Apple) Byte 7: SubType: 0x2 (iBeacon) Byte 8: SubType Length: 0x15 Byte 9-24: Proximity UUID Byte 25-26: Major Byte 27-28: Minor Byte 29: Signal Power
ManufactureData : 設備廠商的自定義數據
使用addManufactureData(int manufacturerId, byte[] manufacturerSpecificData);
第一個參數0x004c,是廠商id,id長度爲2個字節,不足2個字節系統會補0.
(好比id傳入0xac, 不足兩個字節,輸入廣播時:ac, 00)
1 /** 2 * create AdvertiseDate for iBeacon 3 */ 4 public static AdvertiseData createIBeaconAdvertiseData(UUID proximityUuid, short major, short minor, byte txPower) { 5 6 String[] uuidstr = proximityUuid.toString().replaceAll("-", "").toLowerCase().split(""); 7 byte[] uuidBytes = new byte[16]; 8 for (int i = 1, x = 0; i < uuidstr.length; x++) { 9 uuidBytes[x] = (byte) ((Integer.parseInt(uuidstr[i++], 16) << 4) | Integer.parseInt(uuidstr[i++], 16)); 10 } 11 byte[] majorBytes = {(byte) (major >> 8), (byte) (major & 0xff)}; 12 byte[] minorBytes = {(byte) (minor >> 8), (byte) (minor & 0xff)}; 13 byte[] mPowerBytes = {txPower}; 14 byte[] manufacturerData = new byte[0x17]; 15 byte[] flagibeacon = {0x02, 0x15}; 16 17 System.arraycopy(flagibeacon, 0x0, manufacturerData, 0x0, 0x2); 18 System.arraycopy(uuidBytes, 0x0, manufacturerData, 0x2, 0x10); 19 System.arraycopy(majorBytes, 0x0, manufacturerData, 0x12, 0x2); 20 System.arraycopy(minorBytes, 0x0, manufacturerData, 0x14, 0x2); 21 System.arraycopy(mPowerBytes, 0x0, manufacturerData, 0x16, 0x1); 22 23 AdvertiseData.Builder builder = new AdvertiseData.Builder(); 24 builder.addManufacturerData(0x004c, manufacturerData); 25 26 AdvertiseData adv = builder.build(); 27 return adv; 28 }
六、建立廣播設置:模式,是否可鏈接,功率
setAdvertiseMode(int advertiseMode) 設置廣播的模式,低功耗,平衡和低延遲三種模式; 對應 AdvertiseSettings.ADVERTISE_MODE_LOW_POWER ,ADVERTISE_MODE_BALANCED ,ADVERTISE_MODE_LOW_LATENCY 從左右到右,廣播的間隔會愈來愈短
setConnectable(boolean connectable)
設置是否能夠鏈接。
廣播分爲可鏈接廣播和不可鏈接廣播,通常不可鏈接廣播應用在iBeacon設備上,這樣APP沒法鏈接上iBeacon設備
setTimeout(int timeoutMillis) 設置廣播的最長時間,最大值爲常量AdvertiseSettings.LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000; 180秒 設爲0時,表明無時間限制會一直廣播
setTxPowerLevel(int txPowerLevel) 設置廣播的信號強度 常量有AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW, ADVERTISE_TX_POWER_LOW, ADVERTISE_TX_POWER_MEDIUM, ADVERTISE_TX_POWER_HIGH 從左到右分別表示強度愈來愈強. 舉例:當設置爲ADVERTISE_TX_POWER_ULTRA_LOW時, 手機1和手機2放在一塊兒,手機2掃描到的rssi信號強度爲-56左右, 當設置爲ADVERTISE_TX_POWER_HIGH 時, 掃描到的信號強度爲-33左右, 信號強度越大,表示手機和設備靠的越近
* AdvertiseSettings.ADVERTISE_TX_POWER_HIGH -56 dBm @ 1 meter with Nexus 5
* AdvertiseSettings.ADVERTISE_TX_POWER_LOW -75 dBm @ 1 meter with Nexus 5
* AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM -66 dBm @ 1 meter with Nexus 5
*AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW not detected with Nexus 5
1 public AdvertiseSettings createAdvSettings(boolean connectable, int timeoutMillis) { 2 AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder(); 3 //設置廣播的模式, 功耗相關 4 builder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED); 5 builder.setConnectable(connectable); 6 builder.setTimeout(timeoutMillis); 7 builder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH); 8 AdvertiseSettings mAdvertiseSettings = builder.build(); 9 if (mAdvertiseSettings == null) { 10 Log.e(TAG, "mAdvertiseSettings == null"); 11 } 12 return mAdvertiseSettings; 13 }
七、開始廣播後的回調。提示廣播開啓是否成功。
1 //發送廣播的回調,onStartSuccess/onStartFailure很明顯的兩個Callback 2 private AdvertiseCallback mAdvCallback = new AdvertiseCallback() { 3 public void onStartSuccess(android.bluetooth.le.AdvertiseSettings settingsInEffect) { 4 super.onStartSuccess(settingsInEffect); 5 if (settingsInEffect != null) { 6 Log.d(TAG, "onStartSuccess TxPowerLv=" + settingsInEffect.getTxPowerLevel() + " mode=" + settingsInEffect.getMode() + " timeout=" + settingsInEffect.getTimeout()); 7 } else { 8 Log.d(TAG, "onStartSuccess, settingInEffect is null"); 9 } 10 } 11 12 public void onStartFailure(int errorCode) { 13 super.onStartFailure(errorCode); 14 Log.d(TAG, "onStartFailure errorCode=" + errorCode); 15 16 if (errorCode == ADVERTISE_FAILED_DATA_TOO_LARGE) { 17 Toast.makeText(mContext, "advertise_failed_data_too_large", Toast.LENGTH_LONG).show(); 18 Log.e(TAG, "Failed to start advertising as the advertise data to be broadcasted is larger than 31 bytes."); 19 } else if (errorCode == ADVERTISE_FAILED_TOO_MANY_ADVERTISERS) { 20 Toast.makeText(mContext, "advertise_failed_too_many_advertises", Toast.LENGTH_LONG).show(); 21 Log.e(TAG, "Failed to start advertising because no advertising instance is available."); 22 23 } else if (errorCode == ADVERTISE_FAILED_ALREADY_STARTED) { 24 Toast.makeText(mContext, "advertise_failed_already_started", Toast.LENGTH_LONG).show(); 25 Log.e(TAG, "Failed to start advertising as the advertising is already started"); 26 27 } else if (errorCode == ADVERTISE_FAILED_INTERNAL_ERROR) { 28 Toast.makeText(mContext, "advertise_failed_internal_error", Toast.LENGTH_LONG).show(); 29 Log.e(TAG, "Operation failed due to an internal error"); 30 31 } else if (errorCode == ADVERTISE_FAILED_FEATURE_UNSUPPORTED) { 32 Toast.makeText(mContext, "advertise_failed_feature_unsupported", Toast.LENGTH_LONG).show(); 33 Log.e(TAG, "This feature is not supported on this platform"); 34 35 } 36 } 37 };
注意:對於ios 設備接受廣播,外圍設備仍是要廣播出來一個16位的serviceUUID,由於掃描的時候要用(若是不指定特定服務的UUID,沒有辦法進行後臺持續掃描鏈接).