Наша совместная команда Banwar.org

Связаться с нами

  • (097) ?601-88-87
    (067) ?493-44-27
    (096) ?830-00-01

Статьи

Використання Bluetooth в Android

  1. Огляд Android Bluetooth API
  2. Установка налаштувань Bluetooth адаптера з Android
  3. Організація пошуку доступних bluetooth пристроїв
  4. Запит на з'єднання зі спареним пристроєм
  5. Пошук пристроїв
  6. Включення Bluetooth з програми
  7. з'єднання пристроїв
  8. сервер
  9. клієнт
  10. передача даних

Платформа Android надає розробнику багаті комунікаційні можливості. Для роботи з Bluetooth до складу Android входить потужний API, що дозволяє легко проводити сканування навколишнього простору на предмет наявності готових до з'єднання пристроїв, передачу даних між пристроями та багато іншого.
Робота з Bluetooth складається з чотирьох етапів: установка налаштувань bluetooth адаптера, пошук доступних для з'єднання пристроїв, установка з'єднання, передача даних.

Огляд Android Bluetooth API

Bluetooth API розташовується в пакеті android.bluetooth. До його складу входить кілька класів:

  • BluetoothAdapter - відповідає за роботу з встановленим в телефоні Bluetooth модулем. Примірник цього класу є в будь-якій програмі, що використовує bluetooth. До складу цього класу входять методи, що дозволяють здійснювати пошук доступних пристроїв, запитувати список підключених пристроїв, створювати екземпляр класу BluetoothDevice на підставі відомого MAC адреси і створювати BluetoothServerSocket для очікування запиту на з'єднання від інших пристроїв.
  • BluetoothDevice - клас, що асоціюється з віддаленим Bluetooth пристроєм. Примірник цього класу використовується для з'єднання через BluetoothSocket або для запиту інформації про віддаленому пристрої (ім'я, адреса, клас, стан).
  • BluetoothSocket - інтерфейс для Bluetooth socket, аналогічний TCP сокетів. Це точка з'єднання, що дозволяє обмінюватися даними з віддаленим пристроєм через InputStream і OutputStream.
  • BluetoothServerSocket - представляє відкритий сокет сервера, готовий до обробки вхідного запиту. Для того щоб з'єднати два Android пристрою, одне з них повинно відкрити сокет за допомогою цього класу. Коли віддалений пристрій пошле запит на з'єднання, BluetoothServerSocket поверне об'єкт BluetoothSocket.
  • BluetoothClass - описує основні параметри Bluetooth модуля. Об'єкт цього класу доступний тільки в режимі читання і може бути корисний при визначенні типу пристрою.
  • BluetoothProfile - інтерфейс, який представляє Bluetooth профіль (специфікацію бездротового інтерфейсу для з'єднання пристроїв через Bluetooth). Прикладом профілю може служити Hands-Free profile, який визначає порядок роботи з безпровідною гарнітурою.
  • BluetoothHeadset - забезпечує підтримку bluetooth гарнітур. Включає в себе профілі Bluetooth Headset і Hands-Free (v1.5).
  • BluetoothA2dp - Описує Advanced Audio Distribution Profile, що визначає передачу потоку високоякісних аудіо через bluetooth.
  • BluetoothHealth - визначає proxy для Health Device Profile.
  • BluetoothHealthCallback - абстрактний клас, який можна використовувати для реалізації зворотних викликів від BluetoothHealth. Для того щоб реєструвати зміну стану Bluetooth пристрою потрібно на основі цього класу створити власний і перевизначити в ньому callback методи.
  • BluetoothHealthAppConfiguration - конфігурація, яка використовується для з'єднання з різними медичними bluetooth пристроями.
  • BluetoothProfile. ServiceListener - інтерфейс, який надсилає повідомлення BluetoothProfile IPC клієнтам при їх підключенні і відключенні від сервісу.

В рамках цієї статті ми не будемо заглиблюватися в суть роботи з медичними bluetooth пристроями і зосередимося на перших чотирьох класах.

Установка налаштувань Bluetooth адаптера з Android

Якщо Ви вирішили задіяти в своїй програмі можливості Bluetooth модуля, вам необхідно, перш за все, підключити відповідний пакет API.

import android .bluetooth. *;

Крім цього необхідно дати додатком дозвіл на використання Bluetooth модуля. Для цього в маніфест програми потрібно додати рядок

<Uses-permission android: name = "android.permission.BLUETOOTH" />

Якщо Ви збираєтеся використовувати критичні з точки зору безпеки можливості, наприклад, змінити ім'я пристрою, то потрібно дати більш потужні дозволу BLUETOOTH_ADMIN:

<Uses-permission android: name = "android.permission.BLUETOOTH_ADMIN" />

При використанні дозволу BLUETOOTH_ADMIN, необхідно також вказувати і BLUETOOTH.
Перш ніж з'єднуватися з ким-небудь і передавати дані потрібно переконатися, що ваш телефон має bluetooth модуль. Насамперед при роботі з bluetooth API потрібно створити екземпляр класу BluetoothAdapter

BluetoothAdapter bluetooth = BluetoothAdapter .getDefaultAdapter ();

Якщо ваш телефон не підтримує bluetooth, буде повернуто значення "null". На практиці потрібно завжди перевіряти цю умову, щоб уникнути помилок.

BluetoothAdapter bluetooth = BluetoothAdapter .getDefaultAdapter (); if (bluetooth! = null) {// З Bluetooth все в порядку. }

Навіть якщо ваш апарат оснащений Bluetooth модулем, він може бути недоступний, оскільки користувач просто відключив його. Для перевірки доступності Bluetooth служить метод isEnabled (). У разі, якщо модуль відключений, можна запропонувати користувачеві включити його.

if (bluetooth.isEnabled ()) {
// Bluetooth включений. Працюємо.
}
else
{
// Bluetooth вимкнений. Запропонуємо користувачеві включити його.
Intent enableBtIntent = new Intent (BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult (enableBtIntent, REQUEST_ENABLE_BT);
}

Якщо користувач погодився на включення адаптера, в змінну enableBtIntent буде записано значення RESULT_OK. В іншому випадку - RESULT_CANCELED.
Після того, як всі перевірки виконані, можна приступати до роботи. Давайте, наприклад, відобразимо ім'я та адресу нашого адаптера, викликавши методи getName () і getAddress ().

String status; if (bluetooth .isEnabled ()) {String mydeviceaddress = bluetooth .getAddress (); String mydevicename = bluetooth .getName (); status = mydevicename + ":" + mydeviceaddress; } Else {status = "Bluetooth вимкнений"; } Toast .makeText (this, status, Toast .LENGTH_LONG) .show ();

Якщо додаток має дозвіл BLUETOOTH_ADMIN, ви можете змінити ім'я Bluetooth пристрою за допомогою методу

bluetooth .setName ( "AndroidCoder");

для відображення стану адаптера служить метод BluetoothAdapter.getState (). Цей метод може повертати одне з наступних значень:
STATE_TURNING_ON
STATE_ON
STATE_TURNING_OFF
STATE_OFF
Часто з метою економії заряду батареї Bluetooth вимкнений за замовчуванням. Наступних код створює повідомлення, в якому буде сповіщати про стан адаптера:

String state = bluetooth .getState (); status = mydevicename + ":" + mydeviceaddress + ":" + state;

Організація пошуку доступних bluetooth пристроїв

За допомогою класу BluetoothAdapter, Ви можете знайти віддалене bluetooth пристрій, запустивши сканування або надіславши запит список спарених пристроїв.
При скануванні здійснюється пошук доступних bluetooth модулів навколо вас. Якщо в поле досяжності виявиться пристрій з дозволеним bluetooth, воно відправить у відповідь на запит деяку інформацію про себе: ім'я, клас, свій унікальний MAC адресу. На основі цієї інформації можна організувати з'єднання і передачу даних.
Відразу після установки з'єднання з віддаленим пристроєм, користувачеві буде автоматично показаний запит на з'єднання. У разі позитивної відповіді отримана інформація (ім'я, клас та MAC адреса) зберігається і може потім використовуватися через bluetooth API. Так при наступному сеансі зв'язку з даними віддаленим пристроєм вам вже не доведеться проводити сканування, оскільки необхідний MAC адреса вже буде занесений в базу вашого телефону і його можна просто вибрати зі списку спарених пристроїв.
Необхідно розрізняти поняття спарених і з'єднаних пристроїв. Сприяння пристрої просто знають про існування один одного, мають посилання-ключ, яку можуть використовувати для аутентифікації, і здатні створити шифрування з'єднання один з одним. Сполучені пристрої поділяють один радіоканал і можуть передавати дані один одному. Поточна реалізація bluetooth API вимагає, щоб пристрої були спарені перед з'єднанням. (Парування виконується автоматично, коли ви починаєте шифрування з'єднання через Bluetooth API)

Запит на з'єднання зі спареним пристроєм

Перш ніж приступати до пошуку пристроїв навколо має сенс показати користувачеві список вже відомих системі пристроїв. Цілком можливо, що необхідний телефон буде в цьому списку. Метод getBondedDevices () повертає безліч (Set) пристроїв BluetoothDevice, з якими вже відбувалося з'єднання. Ви можете показати користувачеві цей список, наприклад за допомогою ArrayAdapter:

Set <BluetoothDevice> pairedDevices = mBluetoothAdapter .getBondedDevices (); // Якщо список спарених пристроїв не порожній if (pairedDevices .size ()> 0) {// проходимся в циклі з цього списку for (BluetoothDevice device: pairedDevices) {// Додаємо імена і адреси в mArrayAdapter, щоб показати // через ListView mArrayAdapter .add (device .getName () + "\ n" + device .getAddress ()); }}

Для того щоб ініціювати з'єднання потрібно знати MAC адреса пристрою. У наведеному вище прикладі ці адреси заносяться в Arrayadapter і показуються користувачеві. При бажанні, Ви можете легко дістати будь-яку адресу з цього списку.

Пошук пристроїв

Для того, щоб почати сканування радіодіапазону на предмет наявності доступних пристроїв просто викличте метод startDiscovery (). Сканування відбувається в окремому асинхронному потоці. Метод повертає true, якщо запуск сканування пройшов успішно. Зазвичай процес сканування займає близько 10-15 секунд. Щоб отримати інформацію про знайдені пристроях Ваше застосування повинне зареєструвати BroadcastReceiver для ІНТЕНТ ACTION_FOUND. Цей Интент викликається для кожного знайденого пристрою. Интент містить додаткові поля EXTRA_DEVICE і EXTRA_CLASS, які містять об'єкти BluetoothDevice і BluetoothClass відповідно.

// Створюємо BroadcastReceiver для ACTION_FOUND private final BroadcastReceiver mReceiver = new BroadcastReceiver () {public void onReceive (Context context, Intent intent) {String action = intent .getAction (); // Коли знайдено новий пристрій if (BluetoothDevice .ACTION_FOUND .equals (action)) {// Отримуємо об'єкт BluetoothDevice з ІНТЕНТ BluetoothDevice device = intent .getParcelableExtra (BluetoothDevice .EXTRA_DEVICE); // Додаємо ім'я та адресу в array adapter, щоб показвает в ListView mArrayAdapter .add (device .getName () + "\ n" + device .getAddress ()); }}}; // Реєструємо BroadcastReceiver IntentFilter filter = new IntentFilter (BluetoothDevice .ACTION_FOUND); registerReceiver (mReceiver, filter); // Не забудьте зняти реєстрацію в onDestroy

Пошук Bluetooth пристроїв вимагає багато ресурсів. Як тільки Ви знайшли підходяще пристрій, не забудьте зупинити процес сканування. Це можна зробити за допомогою методу cancelDiscovery (). Крім того, якщо ваш телефон вже знаходиться в з'єднанні з будь-яким пристроєм, сканування може значно звузити ширину пропускання каналу, тому краще утриматися від пошуку нових пристроїв під час з'єднання.

Включення Bluetooth з програми

Сучасні Android смартфони не можуть похвалитися довгим часом роботи, тому всі нормальні люди відключають Bluetooth модуль. Якщо Ви при программрованіі для Android хочете дати своїм користувачам можливість зробити телефон видимим для інших телефонів, викличте за допомогою методу startActivityForResult (Intent, int) Интент ACTION_REQUEST_DISCOVERABLE. В результаті користувачеві буде показано системне вікно із запитом на переклад телефону в режим bluetooth видимості. За замовчуванням цей режим включається на 120 секунд. Цей час можна змінити за передавши ІНТЕНТ додатковий параметр EXTRA_DISCOVERABLE_DURATION. Максимально доступне час складає 3600 секунд. Значення 0 переводить bluetooth модуль вашого телефону в режим постійної видимості. Для прикладу створимо Интент із запитом на перехід в режим видимості на 300 секунд

Intent discoverableIntent = new Intent (BluetoothAdapter .ACTION_REQUEST_DISCOVERABLE); discoverableIntent .putExtra (BluetoothAdapter .EXTRA_DISCOVERABLE_DURATION, 300); startActivity (discoverableIntent);

В результаті виконання цього коду користувачеві буде показаний діалог із запитом. Якщо користувач погодиться, телефон буде переведений в режим видимості, і буде викликаний callback метод onActivityResult (). Як результат методу буде передано число секунд, яке пристрій буде видимим. Якщо користувач відмовиться від пропозиції або станеться помилка, то Интент поверне код RESULT_CANCELED. Переклад пристрою в режим видимості автоматично включає bluetooth адаптер.
Якщо ви хочете отримати повідомлення, при зміні режиму видимості Вашого пристрою, зареєструйте BroadcastReceiver для ІНТЕНТ ACTION_SCAN_MODE_CHANGED. Додаткові поля EXTRA_SCAN_MODE і EXTRA_PREVIOUS_SCAN_MODE дозволяють отримати інформацію про новий і старому стані відповідно. Вони можуть набувати значень SCAN_MODE_CONNECTABLE_DISCOVERABLE, SCAN_MODE_CONNECTABLE або SCAN_MODE_NONE. Перше значення вказує на те, що пристрій перебуває в зоні видимості. Друге - пристрій не доступно для пошуку, але здатне приймати з'єднання. Третє - не доступно для пошуку і не може приймати з'єднання.
Вам не потрібно переводити свій телефон в режим видимості, якщо ви ініціалізіруете з'єднання. Видимим має бути пристрій до якого ви хочете підключитися.

з'єднання пристроїв

Щоб з'єднати два пристрої, ви повинні написати серверну і клієнтську частину коду. Одне з пристроїв має відкрити серверний сокет, а друге - форматувати з'єднання, використовуючи MAC адресу сервера. Сервер і клієнт вважаються з'єднаними, коли вони обидва мають активний BluetoothSocket на одному і тому ж RFCOMM каналі. Після цього вони можуть повчати і відправляти потоки даних. Сервер і клієнт по-різному отримують необхідний BluetoothSocket. Сервер отримує його, коли вхідні повідомлення прийнято. Клієнт - коли відкриває RFCOMM для сервера.

сервер

При з'єднанні пристроїв одне з них повинно вести себе як сервер, тобто утримувати відкритий BluetoothServerSocket. Мета сервера - чекати запиту на вхідне з'єднання, і коли воно підтверджено, створити BluetoothSocket. Після цього BluetoothServerSocket можна закрити. Розглянемо поетапно процедуру з'єднання з точки зору сервера:

  1. Отримати BluetoothServerSocket викликавши метод listenUsingRfcommWithServiceRecord (String, UUID). Перший параметр методу є ідентифікаційне ім'я вашого сервісу. Система автоматично додасть його в базу Service Discovery Protocol (SDP). Зазвичай в якості цього параметра просто вказують назву програми. Другий параметр також ідентифікує сервіс. Цей параметр використовується клієнтом при підтвердженні з'єднання.
  2. Починаємо прослуховувати запит на з'єднання через метод accept (). Це блокуючий метод, який повертає результат або коли з'єднання підтверджено, або коли сталося виняток. З'єднання вважається підтвердженим, коли віддалений пристрій пошле запит на з'єднання з UUID, зазначеним при реєстрації серверного сокета. У разі успіху, accept () повертає налаштований на з'єднання BluetoothSocket.
  3. Якщо Ви хочете прийняти додаткове з'єднання, викличте метод close (). Це призведе до звільнення сокета і всіх його ресурсів, але не закриє з'єднаний BluetoothSocket. На відміну від TCP / IP, RFCOMM дозволяє працювати тільки з одним клієнтом на каналі, тому в більшості випадків має сенс викликати метод close () зрізу ж після установки прийняття сокета.

Оскільки метод accept () є блокуючим, його не варто викликати з потоку головною діяльності, оскільки це призведе до підвисання інтерфейсу. Звичайна вся робота з BluetoothServerSocket і BluetoothSocket виконується в окремому потоці. Щоб припинити виконання методу accept (), викличте метод close () для BluetoothServerSocket (або BluetoothSocket) з будь-якого іншого потоку вашого застосування.
Нижче наведено приклад потоку, який реалізує описаний вище механізм роботи

private class AcceptThread extends Thread {private final BluetoothServerSocket mmServerSocket; public AcceptThread () {// використовуємо допоміжну змінну, яку в подальшому // зв'яжемо з mmServerSocket, BluetoothServerSocket tmp = null; try {// MY_UUID це UUID нашого застосування, це ж значення // використовується в клієнтському додатку tmp = mBluetoothAdapter .listenUsingRfcommWithServiceRecord (NAME, MY_UUID); } Catch (IOException e) {} mmServerSocket = tmp; } Public void run () {BluetoothSocket socket = null; // чекаємо поки не відбудеться помилка або НЕ // буде повернутий сокет while (true) {try {socket = mmServerSocket .accept (); } Catch (IOException e) {break; } // якщо з'єднання було підтверджено if (socket! = Null) {// управлчем з'єднанням (в окремому потоці) manageConnectedSocket (socket); mmServerSocket .close (); break; }}} / ** скасування очікування сокета * / public void cancel () {try {mmServerSocket .close (); } Catch (IOException e) {}}}

У цьому прикладі мається на увазі, що може бути встановлено тільки одне з'єднання, тому після того, як з'єднання підтверджено і отримано BluetoothSocket, додаток посилає його окремому потоку, закриває BluetoothServerSocket і виходить з циклу.
Зверніть увагу, коли accept () повертає BluetoothSocket, сокет вже з'єднаний, тому не потрібно викликати метод connect ().
manageConnectedSocket () являє собою метод, всередині якого потрібно створити потік для передачі даних. Його можлива реалізація буде розглянута нижче.
Ви повинні закрити BluetoothServerSocket відразу ж після завершення прослуховування ефіру на предмет наявності вхідного з'єднання. У наведеному прикладі метод close () викликається відразу після отримання об'єкта BluetoothSocket. Також Вам може знадобитися public метод для зупинки приватного BluetoothSocket.

клієнт

Для ініціалізації з'єднання з віддаленим пристроїв (пристроєм, який тримає відкритим серверний сокет) вам необхідно отримати об'єкт BluetoothDevice, що містить інформацію про нього. Цей об'єкт використовується для отримання BluetoothSocket і ініціалізації з'єднання.
Опишемо процедуру з'єднання:

  1. Отримаємо BluetoothSocket викликавши метод BluetoothDevice.createRfcommSocketToServiceRecord (UUID). Значення параметра UUID повинно збігатися з значенням, зазначеним при виклику listenUsingRfcommWithServiceRecord сервера.
  2. Ініціалізіруем з'єднання, викликавши метод connect (). Після виклику цього методу система буде виконувати SDP пошук на віддаленому пристрої, щоб зіставити UUID. У разі успіху за умови підтвердження запиту з боку сервера буде відкритий RFCOMM канал. Це блокуючий виклик. Якщо з якихось причин з'єднання зірветься або вийде timeout (близько 12 секунд), буде згенеровано виняток.


Як і у випадку з accept, метод connect () слід виконувати в окремому потоці, в іншому випадку може статися підвисання інтерфейсу.
Зауваження. Перш ніж викликати connect () переконайтеся, що в даний момент не відбувається сканування з метою пошуку доступних пристроїв. У разі одночасного виконання цих операцій з'єднання буде встановлюватися набагато повільніше, і ви ризикуєте не вкластися в timeout.
Наведемо приклад клієнтської програми, не започатковано з'єднання

private class ConnectThread extends Thread {private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; public ConnectThread (BluetoothDevice device) {// вікорістовуємо допоміжну змінну, якові в подалі // зв'яжемо з mmSocket, BluetoothSocket tmp = null; mmDevice = device; // отримуємо BluetoothSocket щоб з'єднатися з BluetoothDevice try {// MY_UUID це UUID, який використовується і в сервері tmp = device .createRfcommSocketToServiceRecord (MY_UUID); } Catch (IOException e) {} mmSocket = tmp; } Public void run () {// Скасовуємо сканування, оскільки воно гальмує з'єднання mBluetoothAdapter .cancelDiscovery (); try {// Єднаймося з пристроєм через сокет. // Метод блокує виконання програми до // установки з'єднання або виникнення помилки mmSocket .connect (); } Catch (IOException connectException) {// Неможливо з'єднатися. Закриваємо сокет і виходимо. try {mmSocket .close (); } Catch (IOException closeException) {} return; } // управлчем з'єднанням (в окремому потоці) manageConnectedSocket (mmSocket); } / ** скасування очікування сокета * / public void cancel () {try {mmSocket .close (); } Catch (IOException e) {}}}

Для зупинки сканування ефіру викликається метод cancelDiscovery (). Перед викликом цього методу можна перевірити чи йде сканування за допомогою isDiscovering ().
Після завершення роботи з BluetoothSocket завжди викликайте метод close (). Це допоможе заощадити ресурси телефону.

передача даних

Після успішного з'єднання, кожне з з'єднаних пристроїв має об'єкт BluetoothSocket за допомогою якого легко реалізувати передачу / прийом даних:

  1. За допомогою методів getInputStream () і getOutputStream () підлозі об'єкти InputStream і OutputStream, керуючі передачею через сокет.
  2. Читати і писати дані в потік за допомогою методів read (byte []) і write (byte []).

Ви повинні використовувати окремий потік для читання і запису даних. Це важливо, оскільки методи read (byte []) і write (byte []) є блокуючими і їх виклик в основному потоці може паралізувати вашу програму. Головний цикл в цьому окремому потоці повинен зчитувати дані з InputStream. Для запису в OutputStream має сенс створити окремий public метод.

private class ConnectedThread extends Thread {private final BluetoothSocket mmSocket; private final InputStream mmInStream; private final OutputStream mmOutStream; public ConnectedThread (BluetoothSocket socket) {mmSocket = socket; InputStream tmpIn = null; OutputStream tmpOut = null; // Отримати вхідний і вихідний потоки даних try {tmpIn = socket .getInputStream (); tmpOut = socket .getOutputStream (); } Catch (IOException e) {} mmInStream = tmpIn; mmOutStream = tmpOut; } Public void run () {byte [] buffer = new byte [тисячі двадцять чотири]; // буферний масив int bytes; // bytes returned from read () // Прослуховув InputStream поки не відбудеться виняток while (true) {try {// читаємо з InputStream bytes = mmInStream .read (buffer); // посилаємо прочитані байти головною діяльності mHandler .obtainMessage (MESSAGE_READ, bytes, - 1, buffer). sendToTarget (); } Catch (IOException e) {break; }}} / * Викликаємо цей метод з головною діяльності, щоб відправити дані віддаленого пристрою * / public void write (byte [] bytes) {try {mmOutStream .write (bytes); } Catch (IOException e) {}} / * Викликаємо цей метод з головною діяльності, щоб розірвати з'єднання * / public void cancel () {try {mmSocket .close (); } Catch (IOException e) {}}

У конструкторі створюються об'єкти для роботи з потоками даних, після чого потік оживає вхідні дані. Після того як прочитаний черговий блок даних з вхідного потоку він посилається в головну діяльність посредствам виклику методу Handler батьківського класу. Для відправки даних з головною діяльності просто викликається метод write (). Усередині цього публічного методу відбувається виклик write (byte []). Метод close () також можна викликати з головною діяльності. Він розриває з'єднання.

Переклад: Олександр льодком
джерело: developer.android.com


Новости

Banwar.org
Наша совместная команда Banwar.org. Сайт казино "Пари Матч" теперь доступен для всех желающих, жаждущих волнения и азартных приключений.