Android: Сетевые коммуникации с помощью Nearby (PlayServices API)
Совсем недавно Google предоставила мобильным разработчикам Android новую технологию сетевого обмена данными — Nearby. Мне она стала сразу интересна, так как позволяет устанавливать локальное соединение между Android устройствами без особых заморочек! Нет нужды заставлять пользователя вводить IP адрес и порт, он просто инициирует соединение, а клиенты к нему просто подключаются. На странице описывающей технологию указаны следующие варианты использования: — многопользовательские игры на индивидуальных экранах — игроки играют в сетевые игры каждый со своего устройства, которые объединены в сеть (классика жанра); — многопользовательские игры на общем экране — в данном случае в качестве сервера может выступать GoogleTV, на нём будет происходить основной игровой процесс, а все подключившиеся будут использовать свой телефон/планшет в качестве игрового контроллера (как на фото!); — и конечно для любого обмена данными между различными Android устройствами.Уже сейчас вы можете пропробовать эту технологию в игре Beach Buggy Racing:[embedded content]После того как основной материал статьи был подготовлен, мне стало интересно на сколько хорошо система контролирует очерёдность доставляемых пакетов. Специально для этих целей я подготовил маленькое приложение для пересылки фотографий в виде текста. С одного устройства на другое пересылались десятки тысяч пакетов по 2048 символов каждый. Очерёдность не была нарушена, ни одного пакета не утеряно. За контроль очерёдности доставки пришлось заплатить временем доставки, оно увеличилось.
Рассмотрим принципы работы с Nearby.Дабы не создавать велосипед я взял оригинальный пример и рассмотрел его с переводом всех комментариев.Прежде всего удостоверьтесь что на вашем телефоне имеется последняя версия сервисов GooglePlay — https://play.google.com/store/apps/details? id=com.google.android.gms.Теперь перейдём к основным моментам проекта: В проект добавлена библиотека PlayServices (в файл «build.gradle»), именно она позволяет работать с Nearby:
dependencies { compile fileTree (dir: 'libs', include: ['*.jar']) compile 'com.android.support: appcompat-v7:22.0.0' compile 'com.google.android.gms: play-services:7.0.0' } Работу с Nearby можно разделить на следующие этапы:1) Создание главного объекта доступа — GoogleApiClient. Запуск клиента. Остановка клиента2) Запуск рекламации намерения стать точкой доступа3) Запуск поиска точек для соединения4) Присоединение к точке5) Обработка заявок на присоединение6) Контроль соединения7) Принятие и обработка сообщений от оппонента8) Отправка сообщенияРассмотрим всё по порядку.Создание главного объекта доступа — GoogleApiClient. Запуск клиента. Остановка клиента. Тут всё просто. В конструкторе активности создаём главный объект доступа к Nearby. При старте активности запускаем его, при остановке активности отключаемся от сети.
@Override
protected void onCreate (Bundle savedInstanceState) {
…
mGoogleApiClient = new GoogleApiClient.Builder (this)
.addConnectionCallbacks (this)
.addOnConnectionFailedListener (this)
.addApi (Nearby.CONNECTIONS_API)
.build ();
…
}
@Override
public void onStart () {
super.onStart ();
Log.d (TAG, «onStart»);
mGoogleApiClient.connect ();
}
@Override
public void onStop () {
super.onStop ();
Log.d (TAG, «onStop»);
if (mGoogleApiClient!= null) {
mGoogleApiClient.disconnect ();
}
}
Следующий этап — Запуск рекламации намерения стать точкой доступа, метод startAdvertising:
private void startAdvertising () {
debugLog («startAdvertising»);
if (! isConnectedToNetwork ()) {
debugLog («startAdvertising: not connected to WiFi network.»);
return;
}
// Выделяем идентификатор приложения для активации возможности другим устройствам подключиться к данному.
List
private void startDiscovery () {
debugLog («startDiscovery»);
if (! isConnectedToNetwork ()) {
debugLog («startDiscovery: not connected to WiFi network.»);
return;
}
// Поиск устройств с запущенным сервисом рекламации Nearby соединений по идентификатору приложения.
String serviceId = getString (R.string.service_id);
Nearby.Connections.startDiscovery (mGoogleApiClient, serviceId, TIMEOUT_DISCOVER, this)
.setResultCallback (new ResultCallback
@Override public void onEndpointFound (final String endpointId, String deviceId, String serviceId, final String endpointName) { Log.d (TAG, «onEndpointFound:» + endpointId + »:» + endpointName); // Найдены точки для подключения. Отображаем диалог для пользователя, с выбором конечных устройств для подключения. if (mMyListDialog == null) { // Configure the AlertDialog that the MyListDialog wraps AlertDialog.Builder builder = new AlertDialog.Builder (this) .setTitle («Endpoint (s) Found») .setCancelable (true) .setNegativeButton («Cancel», new DialogInterface.OnClickListener () { @Override public void onClick (DialogInterface dialog, int which) { mMyListDialog.dismiss (); } }); // Создание слушателя для диалога mMyListDialog = new MyListDialog (this, builder, new DialogInterface.OnClickListener () { @Override public void onClick (DialogInterface dialog, int which) { String selectedEndpointName = mMyListDialog.getItemKey (which); String selectedEndpointId = mMyListDialog.getItemValue (which); MainActivity.this.connectTo (selectedEndpointId, selectedEndpointName); mMyListDialog.dismiss (); } }); } mMyListDialog.addItem (endpointName, endpointId); mMyListDialog.show (); } В методе «connectTo» реализован диалог выбора точки к которой возможно подключиться. При выборе одного из варианта переходим к непосредственному подключению: /** * Отправка запроса на подключение к конечному устройству. * @param endpointId — идентификатор устройства к которому необходимо подключиться * @param endpointName — название конечной точки, к которой осуществляется подключение. Параметр используется для оповещения о статусе подключения. * */ private void connectTo (String endpointId, final String endpointName) { debugLog («connectTo:» + endpointId + »:» + endpointName); // Отправка запроса на подключение к удалённому устройству. String myName = null; byte[] myPayload = null; Nearby.Connections.sendConnectionRequest (mGoogleApiClient, myName, endpointId, myPayload, new Connections.ConnectionResponseCallback () { @Override public void onConnectionResponse (String endpointId, Status status, byte[] bytes) { Log.d (TAG, «onConnectionResponse:» + endpointId + »:» + status); if (status.isSuccess ()) { debugLog («onConnectionResponse:» + endpointName + » SUCCESS»); Toast.makeText (MainActivity.this, «Connected to » + endpointName, Toast.LENGTH_SHORT).show (); mOtherEndpointId = endpointId; updateViewVisibility (STATE_CONNECTED); } else { debugLog («onConnectionResponse:» + endpointName + » FAILURE»); } } }, this); } Если всё прошло успешно, то можно начинать обмен сообщениями.Для обработки заявок на присоединение предназначен метод onConnectionRequest:
@Override
public void onConnectionRequest (final String endpointId, String deviceId, String endpointName,
byte[] payload) {
debugLog («onConnectionRequest:» + endpointId + »:» + endpointName);
// Данное устройство является рекламирующим и оно получило запрос на подключение. Показываем диалоговое окно предлагающее пользователю принять заявку на подключение или отклонить запрос.
mConnectionRequestDialog = new AlertDialog.Builder (this)
.setTitle («Connection Request»)
.setMessage («Do you want to connect to » + endpointName + »?»)
.setCancelable (false)
.setPositiveButton («Connect», new DialogInterface.OnClickListener () {
@Override
public void onClick (DialogInterface dialog, int which) {
byte[] payload = null;
Nearby.Connections.acceptConnectionRequest (mGoogleApiClient, endpointId,
payload, MainActivity.this)
.setResultCallback (new ResultCallback
@Override public void onMessageReceived (String endpointId, byte[] payload, boolean isReliable) { // Сообщение, полученное от подключённой точки debugLog («onMessageReceived:» + endpointId + »:» + new String (payload)); } Отправка сообщений осуществляется с помощью двух методов:1) Nearby.Connections.sendReliableMessage — отправка надёжных сообщений;2) Nearby.Connections.sendUnreliableMessage — отправка ненадёжных сообщений.При использовании первого метода, система сама контролирует правильность очерёдности доставляемых сообщений, во втором случае последовательность может нарушиться, так как контроля никакого нет. Зато второй метод быстрее, поэтому его лучше использовать когда требуется отправлять большое количество сообщений, например при отправке положения курсора на экране.В ресурсах необходимо указать идентификатор сервиса по которому будет происходить поиск и подключения клиентов.
Спасибо за помощь в подготовке материала inatale!