Главная » Разработка для Android » Передача данных с помощью BluetoothSocket

0

Установив между собой соединение, оба устройства, клиентское и сервер- ное, получат в распоряжение объекты BluetoothSocket. С этого момента все различия между ними нивелируются: с помощью объектов BluetoothSocket каждое устройство может отправлять и принимать данные.

Передача  данных  с помощью  Bluetooth-сокетов управляется стан- дартными для Java объектами InputStream и OutputStream, которые вы можете получить  из BluetoothSocket, используя методы getInputStream и getOutputStream соответственно.

В листинге 13.11 показаны два простых каркасных метода: первый исполь- зуется для отправки строки на удаленное устройство с помощью OutputStream, второй — для ожидания  входящих  строк (с помощью InputStream). Такой подход можно применять для передачи любых потоковых данных.

Листинг 13.11. Отправка и получение  строк с использованием BluetoothSocket

private void sendMessage(String message){ OutputStream outStream;

try {

outStream = socket.getOutputStream();

// Добавьте контрольный символ для остановки. byte[] byteArray = (message + " ").getBytes(); byteArray[byteArray.length – 1] = 0;

outStream.write(byteArray);

} catch (IOException e) { }

}

private String listenForMessage() String result = "";

int bufferSize = 1024;

byte[] buffer = new byte[bufferSize];

try {

InputStream instream = socket.getInputStream();

int bytesRead = -1;

while (true) {

bytesRead = instream.read(buffer);

if (bytesRead != -1) {

while ((bytesRead == bufferSize) && (buffer[bufferSize-1] != 0)){

message = message + new String(buffer, 0, bytesRead);

bytesRead = instream.read(buffer);

}

message = message + new String(buffer, 0, bytesRead – 1);

return result;

}

}

} catch (IOException e) {}

return result;

}

Передача данных через Bluetooth

В следующем примере1 с помощью API для работы с Bluetooth, предо- ставляемых платформой Android, создается простая одноранговая система обмена сообщениями между двумя спаренными Bluetooth-устройствами.

К сожалению, эмулятор Android на сегодняшний день не может приме- няться для тестирования возможностей Bluetooth. Чтобы проверить работу этого приложения, необходимы два физических устройства.

1. Начните  с создания  нового проекта  BluetoothTexting, содержаще- го одноименную  Активность. Добавьте  в манифест  два полномочия: BLUETOOTH и BLUETOOTH_ADMIN.

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.paad.chapter13_bluetoothtexting" android:versionCode="1"

android:versionName="1.0">

<application android:icon="@drawable/icon" android:label="@string/app_name">

<activity android:name=".BluetoothTexting" android:label="@string/app_name">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

<uses-sdk android:minSdkVersion="5" />

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

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

</manifest>

2. Отредактируйте ресурс с разметкой main.xml. Он должен содержать элемент  ListView, отображающий список обнаруженных Bluetooth- устройств. Под ним должны находиться две кнопки: одна для запуска ожидающего BluetoothServerSocket, другая для подключения к ожи- дающему серверу.

Добавьте  элементы  управления TextView  и EditText, чтобы читать и передавать сообщения  через установленное соединение.

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent">

1     Все фрагменты  кода в этом примере  — часть проекта Bluetooth Texting  из главы 13, их можно загрузить с сайта Wrox.com.

<EditText android:id="@+id/text_message" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:enabled="false"

/>

<Button android:id="@+id/button_search"

android:text="Search for listener" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_above="@id/text_message"

/>

<Button android:id="@+id/button_listen" android:text="Listen for connection" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_above="@id/button_search"

/>

<ListView android:id="@+id/list_discovered" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_above="@id/button_listen" android:layout_alignParentTop="true"

/>

<TextView android:id="@+id/text_messages" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_above="@id/button_listen" android:layout_alignParentTop="true" android:visibility="gone"

/>

</RelativeLayout>

3. Переопределите метод onCreate для Активности BluetoothTexting. Сде- лайте вызовы  нескольких  методов-заглушек, которые понадобятся для доступа к Bluetooth-устройству, и настройки элементов  пользо- вательского интерфейса.

import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.UUID;

import android.app.Activity;

import android.bluetooth.BluetoothAdapter;

import android.bluetooth.BluetoothDevice;

import android.bluetooth.BluetoothServerSocket;

import android.bluetooth.BluetoothSocket; import android.content.BroadcastReceiver; import android.content.Context;

import android.content.Intent;

import android.content.IntentFilter;

import android.os.AsyncTask;

import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.KeyEvent; import android.view.View;

import android.view.View.OnClickListener; import android.view.View.OnKeyListener; import android.widget.AdapterView;

import android.widget.ArrayAdapter;

import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.TextView;

import android.widget.AdapterView.OnItemClickListener;

public class BluetoothTexting extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);

// Получите объект BluetoothAdapter configureBluetooth();

// Настройте элемент ListView, который будет содержать обнаруженные

// устройства setupListView();

// Настройке кнопку для поиска устройств setupSearchButton();

// Настройте кнопку для начала ожидания setupListenButton();

}

private void configureBluetooth() {} private void setupListenButton() {} private void setupListView() {} private void setupSearchButton() {}

}

4. Наполните кодом заглушку  configureBluetooth, чтобы получить  до- ступ к локальному Bluetooth-адаптеру и сохранить  его в виде поля класса (его предварительно нужно создать).  Оно будет содержать либо серверный,  либо клиентский (если  соединение  было установ- лено) сокет. Вам также необходимо создать поле для UUID, чтобы идентифицировать приложение при подключении.

private BluetoothAdapter bluetooth;

private BluetoothSocket socket;

private UUID uuid = UUID.fromString("a60f35f0-b93a-11de-8a39-

08002009c666");

private void configureBluetooth() {

bluetooth = BluetoothAdapter.getDefaultAdapter();

}

5. Создайте новый метод switchUI, который будет вызываться при уста- новлении соединения, чтобы сделать возможным использование Пред- ставлений для чтения и отправки  сообщений.

private void switchUI() {

final TextView messageText = (TextView)findViewById(R.id.text_

messages);

final EditText textEntry = (EditText)findViewById(R.id.text_message);

messageText.setVisibility(View.VISIBLE); list.setVisibility(View.GONE); textEntry.setEnabled(true);

}

6. Заполните заглушку  setupListenButton, создав серверный  сокет для ожидания соединения. При вызове этого метода пользователю предло- жат сделать устройство доступным для обнаружения. После закрытия диалогового  окна откройте  BluetoothServerSocket для ожидания  за- просов на подключение (на протяжении определенного времени). Как только соединение  установится, вызовите  метод switchUI, который вы создали в пункте 5.

private static int DISCOVERY_REQUEST = 1;

private void setupListenButton() {

Button listenButton = (Button)findViewById(R.id.button_listen);

listenButton.setOnClickListener(new OnClickListener() {

public void onClick(View view) {

intent disc;

disc = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);

startActivityForResult(disc, DISCOVERY_REQUEST);

}

});

}

@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

if (requestCode == DISCOVERY_REQUEST) { boolean isDiscoverable = resultCode > 0; if (isDiscoverable) {

String name = "bluetoothserver";

try {

final BluetoothServerSocket btserver =

bluetooth.listenUsingRfcommWithServiceRecord(name, uuid);

AsyncTask<Integer, Void, BluetoothSocket> acceptThread =

new AsyncTask<Integer, Void, BluetoothSocket>() {

@Override

protected BluetoothSocket doInBackground(Integer … params) {

try {

socket = btserver.accept(params[0]*1000);

return socket;

} catch (IOException e) { Log.d("BLUETOOTH", e.getMessage());

}

return null;

}

@Override

protected void onPostExecute(BluetoothSocket result) {

if (result != null)

switchUI();

}

};

acceptThread.execute(resultCode);

} catch (IOException e) { Log.d("BLUETOOTH", e.getMessage());

}

}

}

}

7. Создайте клиентский код для подключения.  С его помощью клиент- ское устройство сможет искать ожидающий сервер, сканируя и выводя на экран все доступные устройства.

7.1.Начните  с создания  поля для хранения  списка обнаруженных

Bluetooth-устройств.

private ArrayList<BluetoothDevice> foundDevices;

7.2.Затем наполните кодом заглушку setupListView. Создайте новый ArrayAdapter, связывающий элемент ListView со списком обна- руженных устройств.

private ArrayAdapter<BluetoothDevice> aa;

private ListView list;

private void setupListView() {

aa = new ArrayAdapter<BluetoothDevice>(this, android.R.layout.simple_list_item_1,

foundDevices);

list = (ListView)findViewById(R.id.list_discovered);

list.setAdapter(aa);

}

7.3. Создайте новый Приемник широковещательных намерений, который от- слеживает действие BluetoothDevice.EXTRA_DEVICE и добавляет каждое обнаруженное устройство в список, созданный на шаге 7.1, и оповещает ArrayAdapter, созданный в предыдущем шаге.

BroadcastReceiver discoveryResult = new BroadcastReceiver() {

@Override

public void onReceive(Context context, Intent intent) { BluetoothDevice remoteDevice;

remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

if (bluetooth.getBondedDevices().contains(remoteDevice)) {

foundDevices.add(remoteDevice);

aa.notifyDataSetChanged();

}

}

};

7.4.Закончите заглушку  setupSearchButton,  зарегистрировав объект BroadcastReceiver из предыдущего пункта и инициировав процесс обнаружения.

private void setupSearchButton() {

Button searchButton = (Button)findViewById(R.id.button_search);

searchButton.setOnClickListener(new OnClickListener() {

public void onClick(View view) {

registerReceiver(discoveryResult,

new IntentFilter(BluetoothDevice.ACTION_FOUND));

if (!bluetooth.isDiscovering()) { foundDevices.clear(); bluetooth.startDiscovery();

}

}

});

}

8. Чтобы  завершить  написание  кода, контролирующего процесс под- ключения,  расширьте  метод setupListView из пункта  7.2. Добавьте в него обработчик onItemClickListener, который в асинхронном режи- ме будет пытаться инициировать подключение клиентского адаптера к выбранному  удаленному  Bluetooth-устройству. В случае успеха сохраните ссылку на создающийся при этом сокет и сделайте вызов метода switchUI, созданного в пункте 5.

private void setupListView() {

aa = new ArrayAdapter<BluetoothDevice>(this, android.R.layout.simple_list_item_1, foundDevices);

list = (ListView)findViewById(R.id.list_discovered);

list.setAdapter(aa);

list.setOnItemClickListener(new OnItemClickListener() {

public void onItemClick(AdapterView<?> arg0, View view, int index, long arg3) {

AsyncTask<Integer, Void, Void> connectTask =

new AsyncTask<Integer, Void, Void>() {

@Override

protected Void doInBackground(Integer … params) {

try {

BluetoothDevice device = foundDevices.get(params[0]); socket = device.createRfcommSocketToServiceRecord(uuid); socket.connect();

} catch (IOException e) {

Log.d("BLUETOOTH_CLIENT", e.getMessage());

}

return null;

}

@Override

protected void onPostExecute(Void result) {

switchViews();

}

});

}

}

};

connectTask.execute(index);

9. Запустив это приложение на двух устройствах, можете нажать кноп- ку Listen for connection на одном и Search for listener на другом. В элементе ListView должны отобразиться все связанные адаптеры поблизости, как показано на рис. 13.5.

Рис. 13.5.

Если вы выберете устройство из списка, между ним и вашим локаль- ным адаптером будет установлено соединение. В следующих пунктах этот канал связи понадобится для обмена простыми  текстовыми со- общениями.

10. Начните с расширения метода switchUI. Добавьте новый обработчик нажатий  к элементу  EditText, чтобы следить за событиями  манипу- лятора  D-pad  (джойстика). Как только такое событие  произойдет, получите  текст, содержащийся в элементе EditText, и отправьте  его через BluetoothSocket.

private void switchUI() {

final TextView messageText = (TextView)findViewById(R.id.text_

messages);

final EditText textEntry = (EditText)findViewById(R.id.text_message);

messageText.setVisibility(View.VISIBLE); list.setVisibility(View.GONE); textEntry.setEnabled(true);

textEntry.setOnKeyListener(new OnKeyListener() {

public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {

if ((keyEvent.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_DPAD_CENTER)) {

sendMessage(socket, textEntry.getText().toString());

textEntry.setText("");

return true;

}

});

}

}

return false;

private void sendMessage(BluetoothSocket socket, String msg) { OutputStream outStream;

try {

outStream = socket.getOutputStream();

byte[] byteString = (msg + " ").getBytes(); stringAsBytes[byteString.length – 1] = 0; outStream.write(byteString);

} catch (IOException e) {

Log.d("BLUETOOTH_COMMS", e.getMessage());

}

}

11. Чтобы  принять  отосланное  сообщение, потребуется  создать объект BluetoothSocketListener, работающий  в асинхронном  режиме и сле- дящий за тем, принимает  ли данные BluetoothSocket.

11.1.Начните с нового класса MessagePoster, реализующего интерфейс Runnable. Его конструктор должен принимать два параметра: эле- мент TextView  и строку  с сообщением.  Полученное сообщение нужно вставить в TextView. Этот класс будет использоваться для добавления  входящих сообщений в пользовательский интерфейс из фонового потока.

private class MessagePoster implements Runnable {

private TextView textView;

private String message;

public MessagePoster(TextView textView, String message) {

this.textView = textView;

this.message = message;

}

public void run() {

textView.setText(message);

}

}

11.2. Теперь создайте BluetoothSocketListener, реализующий интерфейс Runnable. Его конструктор должен принимать объект BluetoothSocket (который и будет отслеживаться), элемент TextView  для отобра- жения входящих сообщений и объект Handler для синхронизации при обновлении пользовательского интерфейса.

Получив новое сообщение, отобразите его в элементе TextView, ис- пользуя объект MessagePoster из предыдущего пункта.

private class BluetoothSocketListener implements Runnable {

private BluetoothSocket socket; private TextView textView; private Handler handler;

public BluetoothSocketListener(BluetoothSocket socket,

Handler handler, TextView textView) {

this.socket = socket; this.textView = textView; this.handler = handler;

}

public void run() {

int bufferSize = 1024;

byte[] buffer = new byte[bufferSize];

try {

InputStream instream = socket.getInputStream();

int bytesRead = -1; String message = ""; while (true) {

message = "";

bytesRead = instream.read(buffer);

if (bytesRead != -1) {

while ((bytesRead==bufferSize)&&(buffer[bufferSize-1] != 0)) { message = message + new String(buffer, 0, bytesRead); bytesRead = instream.read(buffer);

}

message = message + new String(buffer, 0, bytesRead – 1); handler.post(new MessagePoster(textView, message)); socket.getInputStream();

}

}

} catch (IOException e) { Log.d("BLUETOOTH_COMMS", e.getMessage());

}

}

}

11.3.В завершение  сделайте  еще одно дополнение  к методу swichUI, создав и запустив новый объект BluetoothSocketListener из предыду- щего пункта.

private Handler handler = new Handler();

private void switchUI() {

final TextView messageText = (TextView)findViewById(R.id.text_

messages);

final EditText textEntry = (EditText)findViewById(R.id.text_message);

messageText.setVisibility(View.VISIBLE); list.setVisibility(View.GONE); textEntry.setEnabled(true);

textEntry.setOnKeyListener(new OnKeyListener() {

public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {

if ((keyEvent.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_DPAD_CENTER)) {

sendMessage(socket, textEntry.getText().toString());

textEntry.setText("");

return true;

}

});

}

return false;

BluetoothSocketListener bsl = new BluetoothSocketListener(socket, handler, messageText);

Thread messageListener = new Thread(bsr);

messageListener.start();

}

Теперь, запустив  приложение, вы сможете настроить  одно устройство на ожидание  подключения,  а другое — на инициирование соединения. Как только связь установится, можно обмениваться текстовыми сообще- ниями.

Имейте в виду, что данный пример максимально упрощен, чтобы основ- ной акцент сделать на описание принципов работы с Bluetooth. Для улучше- ния реализации этого приложения можно перенести весь код, отвечающий за соединение, в отдельный  Cервис, отменяя  регистрацию  Приемника широ- ковещательных намерений после взаимодействия устройств и установления между ними связи.

Источник: Майер P. Android 2 : программирование приложений для планшетных компьютеров и смартфонов : [пер. с англ. ] / Рето Майер. — М. : Эксмо, 2011. — 672 с. — (Мировой компьютерный бестселлер).

По теме:

  • Комментарии