Включение 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). Первый параметр метода представляет собой идентификационное имя вашего сервиса. Система автоматически добавит его в базу ServiceDiscoveryProtocol (SDP). Обычно в качестве этого параметра просто указывают название приложения. Второй параметр также идентифицирует сервис. Этот параметр используется клиентом при подтверждении соединения.

2. Начинаем прослушивать запрос на соединения через метод accept(). Это блокирующий метод, который возвращает результат либо когда соединение подтверждено, либо когда произошло исключение. Соединение считается подтвержденным, когда удаленное устройство пошлет запрос на соединение с UUID, указанным при регистрации серверного сокета. В случае успеха, accept() возвращает настроенный на соединение BluetoothSocket.

3. Если Вы хотите принять дополнительное соединение, вызовите метод close(). Это приведет к освобождению сокета и всех его ресурсов, но не закроет соединенный BluetoothSocket. В отличие от TCP/IP, RFCOMM позволяет работать только с одним клиентом на канале, поэтому в большинстве случаев имеет смысл вызывать метод close() срезу же после установки принятия сокета.

Поскольку метод accept() является блокирующим, его не стоит вызывать из потока главной деятельности, поскольку это приведет к подвисанию интерфейса. Обычна вся работа с BluetoothServerSocket и BluetoothSocket выполняется в отдельном потоке. Чтобы прекратить выполнение метода accept(), вызовите метод close() для BluetoothServerSocket (или BluetoothSocket) из любого другого потока вашего приложения.

Ниже приведен пример потока, реализующий описанный выше механизм работы

privateclass AcceptThread extends Thread{

private final BluetoothServerSocketmmServerSocket;

 

public AcceptThread(){

// используем вспомогательную переменную, которую в дальнейшем

// свяжемсmmServerSocket,

BluetoothServerSockettmp= 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.

Приведем пример клиентского приложения, инициализирующего соединение

privateclass ConnectThread extends Thread{

private finalBluetoothSocketmmSocket;

private final BluetoothDevicemmDevice;

 

public ConnectThread(BluetoothDevice device){

// используем вспомогательную переменную, которую в дальнейшем

// свяжемсmmSocket,

BluetoothSockettmp= null;

mmDevice= device;

 

// получаемBluetoothSocketчтобысоединитьсясBluetoothDevice

try{

// MY_UUID это UUID, который используется и в сервере

tmp= device.createRfcommSocketToServiceRecord(MY_UUID);

} catch(IOException e){}

mmSocket= tmp;

}

 

public voidrun(){

// Отменяем сканирование, поскольку оно тормозит соединение

mBluetoothAdapter.cancelDiscovery();

 

try{

// Соединяемся с устройством через сокет.

// Метод блокирует выполнение программы до

// установки соединения или возникновения ошибки

mmSocket.connect();

} catch(IOExceptionconnectException){

// Невозможно соединиться. Закрываем сокет и выходим.

try{

mmSocket.close();

} catch(IOExceptioncloseException){}

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 метод.

privateclass ConnectedThread extends Thread{

private final BluetoothSocketmmSocket;

private final InputStreammmInStream;

private final OutputStreammmOutStream;

 

public ConnectedThread(BluetoothSocket socket){

mmSocket= socket;

InputStreamtmpIn= null;

OutputStreamtmpOut= null;

 

// Получить входящий и исходящий потоки данных

try{

tmpIn= socket.getInputStream();

tmpOut= socket.getOutputStream();

} catch(IOException e){}

 

mmInStream= tmpIn;

mmOutStream= tmpOut;

}

 

public void run(){

byte[] buffer= new byte[1024]; // буферныймассив

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 voidwrite(byte[] bytes){

try{

mmOutStream.write(bytes);

} catch(IOException e){}

}

 

/* Вызываем этот метод из главной деятельности,

чтобы разорвать соединение */

public voidcancel(){

try{

mmSocket.close();

} catch(IOException e){}

}

В конструкторе создаются объекты для работы с потоками данных, после чего поток оживает входящие данные. После того как прочитан очередной блок данных из входящего потока он посылается в главную деятельность посредствам вызова метода Handler родительского класса. Для отправки данных из главной деятельности просто вызывается метод write(). Внутри этого публичного метода происходит вызов write(byte[]). Метод close() также можно вызвать из главной деятельности. Он разрывает соединение.

Полезные ссылки:

https://www.mobilab.ru/androiddev/bluetoothinandroid.html\

https://developer.alexanderklimov.ru/android/bluetooth.php#basic



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2017-11-19 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: