Хотя Bluetooth Low Energy (LE) является относительно новой технологией, она уже зарекомендовала себя как универсальная и полезная коммуникационная среда. Хотя он может использоваться для беспроводного соединения устройств, он также позволяет устройствам выступать в качестве маяков и передавать данные.
В этом руководстве вы узнаете о классе BluetoothLeAdvertiser
, который позволяет разработчикам превратить поддерживаемый телефон в маяк Bluetooth LE без необходимости использования дополнительного оборудования. Вы также узнаете, как сканировать рекламные данные Bluetooth LE, чтобы вы могли соответствующим образом реагировать в своих собственных приложениях. Вы можете скачать исходные файлы для этого урока на GitHub .
1. Настройка проекта
Для этого урока вы можете начать с создания простого пустого проекта в Android Studio. В вашем файле build.gradle необходимо установить минимальную версию SDK равной 21 , поскольку реклама Bluetooth LE не была представлена на Android до выпуска Lollipop. Хотя есть способы обернуть API, которые не поддерживаются до SDK 21, они не будут рассмотрены в этом руководстве. Мы собираемся сосредоточиться только на новой технологии под рукой.
1
2
3
4
5
6
7
|
defaultConfig {
applicationId «com.tutsplus.bleadvertising»
minSdkVersion 21
targetSdkVersion 23
versionCode 1
versionName «1.0»
}
|
Затем вам нужно добавить три разрешения в AndroidManifest.xml проекта. Первые два допускают связь Bluetooth, а третье разрешение, ACCESS_COARSE_LOCATION
, является новым требованием для использования Bluetooth на устройствах Android 6.0 и выше.
1
2
3
|
<uses-permission android:name=»android.permission.BLUETOOTH» />
<uses-permission android:name=»android.permission.BLUETOOTH_ADMIN» />
<uses-permission android:name=»android.permission.ACCESS_COARSE_LOCATION» />
|
Если вы разрабатываете на устройстве под управлением Android 6.0 или выше, вашему приложению требуется разрешение на местоположение, предоставленное пользователем. Инструкции для этого можно найти в разделе «Понимание разрешений в Android M» , который я написал ранее в этом году. Для простоты в этом руководстве будет просто предоставлено разрешение на экране информации о приложении Android.
Теперь, когда у вас есть разрешение на доступ ко всему, что понадобится вашему приложению, пришло время создать файл макета. Откройте файл activity_main.xml и создайте простой макет с двумя кнопками и текстовым представлением.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<?xml version=»1.0″ encoding=»utf-8″?>
<LinearLayout xmlns:android=»http://schemas.android.com/apk/res/android»
android:layout_width=»match_parent»
android:layout_height=»match_parent»
android:orientation=»vertical»>
<Button
android:id=»@+id/advertise_btn»
android:layout_width=»match_parent»
android:layout_height=»wrap_content»
android:text=»Advertise»/>
<Button
android:id=»@+id/discover_btn»
android:layout_width=»match_parent»
android:layout_height=»wrap_content»
android:text=»Discover» />
<TextView
android:id=»@+id/text»
android:layout_width=»wrap_content»
android:layout_height=»wrap_content»
android:text=»test»/>
</LinearLayout>
|
Два объекта Button
используются для запуска рекламы или обнаружения. TextView
используется для отображения данных, найденных при обнаружении. Когда вы закончите заполнение файла макета, вы можете закрыть его и открыть MainActivity.java . Первое, что вы делаете в этом файле, это добавляете реализацию для View.OnClickListener
которая используется кнопками.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onClick(View v) {
if( v.getId() == R.id.discover_btn ) {
discover();
} else if( v.getId() == R.id.advertise_btn ) {
advertise();
}
}
}
|
В описанном выше методе onClick(View v)
advertise()
и discover()
определены далее в этом руководстве. Теперь вы можете добавить заглушки для этих методов, чтобы ваше приложение компилировалось. Затем вам нужно создать переменные-члены в верхней части класса, чтобы отслеживать кнопки и текстовое представление.
1
2
3
|
private TextView mText;
private Button mAdvertiseButton;
private Button mDiscoverButton;
|
Как только вы определили свои представления, вам нужно инициализировать их в onCreate(Bundle savedInstanceState)
и onCreate(Bundle savedInstanceState)
каждую Button
к OnClickListener
вы определили ранее.
01
02
03
04
05
06
07
08
09
10
11
12
|
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mText = (TextView) findViewById( R.id.text );
mDiscoverButton = (Button) findViewById( R.id.discover_btn );
mAdvertiseButton = (Button) findViewById( R.id.advertise_btn );
mDiscoverButton.setOnClickListener( this );
mAdvertiseButton.setOnClickListener( this );
}
|
Наконец, убедитесь, что устройство, которое вы или ваши пользователи используете, поддерживает несколько рекламных объявлений. Хотя функции программного обеспечения, необходимые для рекламы в качестве периферийного устройства, доступны в Lollipop, производители также должны использовать набор микросхем Bluetooth, который может поддерживать рекламу.
Если реклама Bluetooth LE не поддерживается, вам следует отключить две кнопки в вашем приложении. Хотя известно, что эта функция работает на Nexus 6, 5X, 6P и 9, существуют другие телефоны, которые также поддерживают рекламу Bluetooth LE, и дополнительные устройства будут производиться по мере того, как производители будут создавать новые телефоны и планшеты.
1
2
3
4
5
|
if( !BluetoothAdapter.getDefaultAdapter().isMultipleAdvertisementSupported() ) {
Toast.makeText( this, «Multiple advertisement not supported», Toast.LENGTH_SHORT ).show();
mAdvertiseButton.setEnabled( false );
mDiscoverButton.setEnabled( false );
}
|
2. Реклама через Bluetooth LE
Чтобы начать рекламу через Bluetooth LE, вам нужно получить BluetoothLeAdvertiser
из Android BluetoothAdapter
. Вы можете сделать это в методе advertise()
который вызывается при нажатии кнопки рекламы.
1
|
BluetoothLeAdvertiser advertiser = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();
|
Когда у вас есть BluetoothLeAdvertiser
, вы определяете настройки, которые используются при рекламе, такие как мощность, которую антенна должна использовать при трансляции, и как часто устройство должно рекламировать. Класс AdvertiseSettings.Builder
также позволяет вам определить, должно ли мобильное устройство быть подключаемым, что позволяет вам передавать гораздо больше данных между устройствами.
1
2
3
4
5
|
AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setAdvertiseMode( AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY )
.setTxPowerLevel( AdvertiseSettings.ADVERTISE_TX_POWER_HIGH )
.setConnectable( false )
.build();
|
Вы заметите, что этот пример — реклама с высоким уровнем сигнала (мощность передачи) и низкой задержкой. Хотя это делает ваше устройство быстро обнаруживаемым, оно также может потреблять много батареи, что является ценным товаром, когда вы разрабатываете в мобильном пространстве.
Есть и другие варианты режима рекламы. Параметр ADVERTISE_MODE_LOW_POWER
является настройкой по умолчанию для рекламы и передается с наименьшей частотой, чтобы сохранить наибольшую мощность. Опция ADVERTISE_MODE_BALANCED
пытается сберечь энергию, не слишком долго ожидая между рекламными объявлениями.
Мощность TX может быть установлена на сверхнизкое, низкое, среднее или высокое. Каждый уровень соответствует тому, насколько далеко будет виден рекламный пакет Bluetooth LE, хотя точные диапазоны зависят от аппаратного обеспечения устройства.
Затем вы определяете UUID, который используется для идентификации ваших пакетов, когда они объявляются. Вы можете создать новый UUID с помощью утилиты uuidgen из командной строки.
1
2
|
➜ ~ uuidgen
CDB7950D-73F1-4D4D-8E47-C090502DBD63
|
Хотя эта утилита создает 128-битный UUID, система Android использует только 16-битные UUID для рекламы и автоматически настраивает 128-битный UUID для соответствия. В приведенном выше примере 16-битный UUID будет 950D. Затем сохраните 128-битный UUID в виде строки в strings.xml .
1
|
<string name=»ble_uuid»>CDB7950D-73F1-4D4D-8E47-C090502DBD63</string>
|
Как только вы создали UUID, пришло время создать объект ParcelUuid
, AdvertiseData
и передать некоторые дополнительные данные в качестве дополнительной услуги. В этом примере вы просто передаете имя устройства и строку "Data"
(которые должны быть преобразованы в байтовый массив), поскольку размер ваших рекламных пакетов довольно ограничен.
1
2
3
4
5
6
7
|
ParcelUuid pUuid = new ParcelUuid( UUID.fromString( getString( R.string.ble_uuid ) ) );
AdvertiseData data = new AdvertiseData.Builder()
.setIncludeDeviceName( true )
.addServiceUuid( pUuid )
.addServiceData( pUuid, «Data».getBytes( Charset.forName( «UTF-8» ) ) )
.build();
|
Последнее, что вы делаете для рекламы через Bluetooth LE на вашем устройстве, это создаете обратный вызов, который прослушивает успех или неудачу при рекламе, а затем запускаете рекламу через BluetoothLeAdvertiser
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
AdvertiseCallback advertisingCallback = new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
}
@Override
public void onStartFailure(int errorCode) {
Log.e( «BLE», «Advertising onStartFailure: » + errorCode );
super.onStartFailure(errorCode);
}
};
advertiser.startAdvertising( settings, data, advertisingCallback );
|
На этом этапе вы сможете запустить свое приложение и начать рекламу через Bluetooth LE. Тем не менее, следующим шагом в этом руководстве будет поиск рекламных объявлений, чтобы вы могли отобразить эту информацию.
3. Обнаружение рекламы Bluetooth LE
Прежде чем вы начнете обнаруживать службы Bluetooth LE, вам нужно добавить еще несколько переменных-членов в начало вашего класса.
1
2
|
private BluetoothLeScanner mBluetoothLeScanner;
private Handler mHandler = new Handler();
|
mBluetoothLeScanner
используется для сканирования пакетов Bluetooth LE, а mHandler
управляет небольшим таймером, который останавливает обнаружение по истечении заданного периода времени.
В дополнение к этим переменным-членам вам необходимо создать новый ScanCallback
в верхней части вашего класса, чтобы впоследствии к нему в вашем приложении мог обращаться внутренний класс. Этот обратный вызов получает результат обнаруженной приемлемой рекламы и отображает имя рекламного устройства и связанные данные в текстовом представлении.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if( result == null
||
||
return;
StringBuilder builder = new StringBuilder( result.getDevice().getName() );
builder.append(«\n»).append(new String(result.getScanRecord().getServiceData(result.getScanRecord().getServiceUuids().get(0)), Charset.forName(«UTF-8»)));
mText.setText(builder.toString());
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
}
@Override
public void onScanFailed(int errorCode) {
Log.e( «BLE», «Discovery onScanFailed: » + errorCode );
super.onScanFailed(errorCode);
}
};
|
Затем инициализируйте onCreate(Bundle savedInstanceState)
в onCreate(Bundle savedInstanceState)
как показано ниже.
1
|
mBluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner();
|
После того, как вы инициализировали сканер Bluetooth, вы можете начать использовать метод discover()
. Для этого примера приложения вы создаете объект ScanFilter
и помещаете его в List
так, чтобы ваше приложение ScanFilter
только на те рекламные пакеты, которые вас интересуют.
1
2
3
4
|
ScanFilter filter = new ScanFilter.Builder()
.setServiceUuid( new ParcelUuid(UUID.fromString( getString(R.string.ble_uuid ) ) ) )
.build();
filters.add( filter );
|
Вам также необходимо создать объект ScanSettings
, который работает аналогично объекту AdvertiseSettings
который вы создали ранее в этом руководстве.
1
2
3
|
ScanSettings settings = new ScanSettings.Builder()
.setScanMode( ScanSettings.SCAN_MODE_LOW_LATENCY )
.build();
|
Установив фильтры, настройки и обратные вызовы, вы можете приступить к поиску рекламы Bluetooth LE.
1
|
mBluetoothLeScanner.startScan(filters, settings, mScanCallback);
|
Если вы сейчас запускаете свое приложение на двух устройствах, которые поддерживают рекламу Bluetooth LE и выбирают одно для рекламы, а другое для обнаружения, обнаруживающее устройство должно найти рекламодателя и отобразить объявленные данные. На следующем снимке экрана Nexus 5X обнаруживает рекламу Nexus 6.
Последнее, что вам нужно сделать, это создать новый объект Runnable
и связать его с mHandler
. Обработчик ждет десять секунд, а затем запускает Runnable
который останавливает обнаружение. Причиной этого является то, что обнаружение может быстро разрядить аккумулятор устройства. Вам нужно только попытаться обнаружить рекламные пакеты в течение коротких периодов времени или когда вы ожидаете найти рекламное устройство.
1
2
3
4
5
6
|
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mBluetoothLeScanner.stopScan(mScanCallback);
}
}, 10000);
|
Вывод
Хотя этот пример может показаться не слишком привлекательным, вы только что создали полностью работающее приложение, которое рекламирует через Bluetooth LE, чтобы другие устройства могли его обнаруживать и передавать данные. Несмотря на то, что в этом учебном пособии речь шла о рекламе и поиске с Android, вы также можете легко создать аналогичное приложение для iOS, чтобы обмениваться данными между двумя платформами или создать собственное устройство с поддержкой Bluetooth LE, которое может взаимодействовать с мобильными телефонами и планшетами.
Поскольку эта технология продолжает развиваться и быть интегрированной в карманы каждого, у разработчиков будут бесконечные возможности делать действительно интересные вещи. Для получения дополнительной информации о работе с дополнительной технологией Bluetooth на Android, проверьте Создание Bluetooth-сканера с помощью Bluetooth API Android от Мэтью Кима .