пятница, 3 апреля 2015 г.

App Tethering #3: Передача и получение данных

Продолжаем изучать компоненты App Tethering. В этой статье мы рассмотри способы передачи данных удалённым приложениям.










Список статей:

p.s. Все права на картинку принадлежат Embarcadero Technologies, Inc.


Передача данных.
Существует два способа передачи данных через App Tethering:
  • Задать постоянный ресурс. Удалённые приложения могут считывать данный ресурс, а также подписываться на обновления этого ресурса.
  • Передавать данные как временный ресурс. Отправлять можно строки или потоки. Удалённые приложения не могут подписаться на обновления таких ресурсов. Как правило, такой ресурс отправляется единожды.

Теперь давайте рассмотрим оба способа на примере. 

Задаём постоянный ресурс.
Чтобы добавить такой ресурс, его нужно поместить в обёртку «TLocalResource». Поддерживаются такие типы данных: Boolean, Integer, Int64, Single, Double, String, TStream.

Шаги:
  1. Дважды щёлкаем по свойству «Resources» у компонента «TTetheringAppProfile»
  2. В открывшемся редакторе, жмём кнопку «Создать», чтобы добавить новый «TLocalResource» в список
  3. Задаём уникальное имя
Теперь ресурс будет доступен удалённым приложениям, они смогут читать его значение и подписываться на обновления ресурса.

После того как мы определили ресурс во время разработки (design time) приложения, мы должны присвоить ему какое-то значение во время выполнения (run time) приложения, сделать это можно так:
TetheringAppProfile1.Resources.FindByName('ResourceName').Value := 'SomeValue';
где,
  • ResourceName – это уникальное Имя ресурса
  • SomeValue – это значение, которые мы хотим присвоить ресурсу. Это значение может быть любого из перечисленных выше типов.

Подключенные приложения могут запрашивать значение ресурса. Если приложение подписалось на получение обновлений этого ресурса, то при изменении значения ресурса оно автоматически будет отправлено удалённому приложению.

Передаём данные как временный ресурс.
Для подобной передачи данных предусмотрены методы «SendString» и «SendStream» у компонента «TTetheringAppProfile».

Данные методы должны получить следующие параметры:
  • <AProfile>  - это удалённый профиль, которому мы высылаем временный ресурс
  • <Description> - строка, описывающая отправляемое значение
  • <AString> или <AStream> -  отправляемое значение, строка, либо поток

Получение данных.

Запрос удалённых ресурсов.
  • Метод «GetRemoteResourceValue» у компонента «TTetheringAppProfile» позволяет получить значение удалённого ресурса.
  • Метод «GetRemoteProfileResources» у компонента «TTetheringAppProfile» позволяет получить список доступных ресурсов.
Обработка входящих удалённых ресурсов.
При получении ресурса срабатывают два события «OnAcceptResource» и «OnResourceReceived» у компонента «TTetheringAppProfile» . Для обработки входящих ресурсов необходимо написать обработчики для этих событий.


Получение или отклонение входящего удалённого ресурса.
Вы можете написать обработчик события «OnAcceptResource», чтобы решить, хотите ли вы принимать ресурс или нет.


Чтение входящего удалённого ресурса.
Если вы не обрабатываете событие «OnAcceptResource» или вы не запретили получение ресурса, то сработает событие «OnResourceReceived». Вы можете обрабатывать это событие, чтобы прочитать входящий ресурс.

Подписаться на обновления удалённого ресурса.
Подписаться можно двумя способами:
  1. Указав для ресурса свойство «Kind: Mirror». Тут ситуация такая же как с Действиями в предыдущей статье. Если основной ресурс изменится, то второстепенный ресурс пример такое же значение, при этом сработает событие «TLocalResource.OnResourceReceived» у  второстепенного ресурса. При этом приложения должны быть в одной группе и ресурсы должны иметь одинаковые имена.
  2. Подписаться при помощи метода «SubscribeToRemoteItem» у компонента «TTetheringAppProfile».
Я создал простой пример, демонстрирующий работу с постоянным и временным ресурсом.
Все настройки компонентов «TTetheringManager» и «TTetheringAppProfile» стандартны, т.е. как в предыдущих статьях. В каждом проекте добавятся: 1 – TEdit, 1 - TButton 


Алгоритм работы этих проектов такой:
  1. Соединяемся
  2. Проект «Client» автоматически подписывается на постоянные ресурс проекта «Server»
  3. Заполняем поле «Edit1» и жмём кнопку, таким образом, мы отправляем текст из «Edit1» как временный ресурс.
  4. Проект «Server» при получении временного ресурса, добавляет его в свой компонент Memo и одновременно изменяет значение своего постоянного ресурса (на любое значение).
  5. Далее, т.к. постоянное значение изменилось, происходит автоматическая отправка этого значения в проект «Client». Получив новое значение, оно будет добавлено в компонент Memo проекта «Client».

Проект «Server»
Структура


Код
procedure TForm2.FormShow(Sender: TObject);
begin
  Label1.Text := 'Local Manager: ' + TMServer.Identifier;
  TMServer.AutoConnect();
end;

procedure TForm2.TAPServerResourceReceived(const Sender: TObject;
  const AResource: TRemoteResource);
begin
  if AResource.ResType = TRemoteResourceType.Data then
  begin
    Memo1.Lines.Add(AResource.Hint + ': ' + AResource.Value.AsString);
    TAPServer.Resources.FindByName('TestSend').Value := '[Server] Постоянный ресурс';
  end;
end;

procedure TForm2.TMServerPairedFromLocal(const Sender: TObject;
  const AManagerInfo: TTetheringManagerInfo);
begin
  Memo1.Lines.Add('PairedFromLocal: ' +
      AManagerInfo.ManagerIdentifier + ' ' +
      AManagerInfo.ManagerText);
end;

procedure TForm2.TMServerPairedToRemote(const Sender: TObject;
  const AManagerInfo: TTetheringManagerInfo);
begin
  Memo1.Lines.Add('PairedToRemote: ' +
      AManagerInfo.ManagerIdentifier + ' ' +
      AManagerInfo.ManagerText);
end;


Проект «Client»
Структура



Код
procedure TForm1.Button1Click(Sender: TObject);
begin
  TAPClient.SendString(TAPClient.ConnectedProfiles[0], '[Client] Временный ресурс', Edit1.Text);
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  Label1.Text := 'Local Manager: ' + TMClient.Identifier;
  TMClient.AutoConnect();
end;

procedure TForm1.TAPClientResources0ResourceReceived(const Sender: TObject;
  const AResource: TRemoteResource);
begin
  Memo1.Lines.Add(AResource.Value.AsString);
end;

procedure TForm1.TMClientPairedFromLocal(const Sender: TObject;
  const AManagerInfo: TTetheringManagerInfo);
begin
  Memo1.Lines.Add('PairedFromLocal: ' +
      AManagerInfo.ManagerIdentifier + ' ' +
      AManagerInfo.ManagerText);
end;

procedure TForm1.TMClientPairedToRemote(const Sender: TObject;
  const AManagerInfo: TTetheringManagerInfo);
begin
  Memo1.Lines.Add('PairedToRemote: ' +
      AManagerInfo.ManagerIdentifier + ' ' +
      AManagerInfo.ManagerText);
end;

Запускаем  проекты «Server» и «Client», жмём кнопку и видим результат:

Исходники примера: Скачать с Google Drive

Ничего сложного в использовании App Tethering нет, но как всегда есть нюансы и возможно баги...

В общем, на этом серия статей по App Tethering завершена. Если ещё что-то придёт в голову, то вы об этом обязательно узнаете. 

8 комментариев:

  1. Подскажите как перехватывать исключения при использовании App Tethering? Попытка запуска с выключенным wifi приводит полному зависанию приложения. При отладке генерируется EIdSocketError, но перехватить его не возможно :-(

    ОтветитьУдалить
    Ответы
    1. Так вы перед запуском проверяйте, включен WiFi или нет. По поводу исключений, постараюсь посмотреть.

      Удалить
  2. Андрей, отличные статьи!
    Можно вопрос? Подскажите пожалуйста, а как передать файл в потоке при использовании App Tethering ?

    ОтветитьУдалить
    Ответы
    1. Используйте метод TTetheringAppProfile.SendStream

      Удалить
  3. Здравствуйте.
    А приведите пример, пожалуйста, с использованием Bluetooth. А то в текущих примерах, если поменять на блутуф и запустить на смартфонах, то ничего не работает. А если оставить на Network, то через WiFi работает. Хотелось бы быстрого и легкого способа передачи строк со смартфона на смартфон.

    ОтветитьУдалить
    Ответы
    1. Потестить пока неначем. Поэтому предположения, везде выставить Bluetooth, не заработает, тогда попробовать добавить разрешения для моб. приложения. Гляньте ещё вот этот пример: C:\Users\Public\Documents\Embarcadero\Studio\18.0\Samples\Object Pascal\Multi-Device Samples\Device Sensors and Services\App Tethering\MediaPlayer
      Раньше он работал, сейчас не знаю...

      Удалить
  4. Права выставлял. И чисто для блутус и потом вообще все.
    Не помогает. Пример с плеером тоже. Ноут бук со смартфоном сопрягаю и не фига.
    АппТетринг через блутус не работают. Если можете, то попробуйте как нибудь

    ОтветитьУдалить
    Ответы
    1. Потестил, мой проект работает через блютус. Демка, которую вам советовал (раньше она работала), завелась только частично...
      Как сделать:
      Выставляем для мобильного приложения 2 разрешения Bluetooth и меняем в обоих проектах Network на Bluetooth.
      Ознакомиться с подробностями можно тут: http://fire-monkey.ru/topic/3374-androidxe10%D0%BF%D0%B5%D1%80%D0%B5%D0%B4%D0%B0%D1%87%D0%B8-%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8-%D0%BC%D0%B5%D0%B6%D0%B4%D1%83-%D1%81%D0%BC%D0%B0%D1%80%D1%82%D1%84%D0%BE%D0%BD%D0%B0%D0%BC%D0%B8/?do=findComment&comment=21175

      Есть ещё одна демка, почти идентичная: C:\Users\Public\Documents\Embarcadero\Studio\18.0\Samples\Object Pascal\RTL\Tethering\MediaPlayer

      Удалить