Отладка мобильных приложений Delphi/C++Builder for Android с помощью CodeSite и DataSnap

Posted by on in Blogs
В предыдущей статье мы бодро сделали:

  • сервер DataSnap – сервер приложений – «обычное» VCL-приложение, способное воспринимать команды «извне»;

  • клиент DataSnap, который вызывает нами созданный метод «удалённо» (настольная машина с запущенным DataSnap-сервером приложений должна быть «на той же сети, что и дивайс клиентского приложения», а именно:

  • Win32-сборка тонкого клиента для отладочных целей;

  • Android-приложение «тонкий клиент».


Сегодня продолжим наши упражнения – разовьём тему отладки мобильных приложений.

CodeSite Express

Данный сервис логгирования/журналирования доступен вместе с Delphi уже давно. Можно ознакомиться с его возможностями от автора (CodeRage 6 session on CodeSite by Ray Konopka), а также по краткой, но ёмкой публикации по CodeStie by Bob Swart. Сейчас мы познакомимся с ним в разрезе отладки мобильных приложений на основе сделанной нами связки DataSnap-клиент – DataSnap-сервер.

Почему это эффективно при разработке мобильных приложений:

  • мобильные приложения в процессе разработки очень хорошо тестируются/отлаживаются в виде Win32-сборки для отработки связи с базами данных, алгоритмов, бизнес-логики и всего того, что не требует реального устройства;

  • но если приложение уже «попало в лапы пользователей» на реальных устройствах, то запуск в режиме отладки достаточно трудоёмок. Здесь желательно «пустить пользователя в свободное плавание», а затем точно отслеживать, что и как он делает и к каким результатам относительно внутренних значений переменных/объектов мобильного приложения это приводит.


Модификация DataSnap-сервера - добавление CodeSite

Откроем проектную группу, созданную в рамках предыдущих упражнений. Мы помним, что DataSnap-сервер является у нас "обычным VCL-приложением", поэтому мы сейчас добавим в него возможность по использованию CodeSite и сразу же экспонируем функциональность "наружу".

На форме разместим новый компонент "кнопка". Мнемонически обзовём её "кнопка для конопки" (надеюсь, Mr. Konopka простит меня, но уж очень рифма хорошая).



Отклик на кнопку будет простой:
uses CodeSiteLogging;

procedure TForm1.Button1Click(Sender: TObject);
begin
CodeSite.Send('hello in CodeSite live logger');
end;

Обращаем внимание на uses CodeSiteLogging. Запускаем приложение как "приложение" (пока не думаем о мобильном клиенте). Кликаем кнопку - видим Конопку!



Помимо нашего окна возникает окно "живого оконного live логгера", где честно отображена посланная в журнал строка. Подробно о том, что и как можно "откидывать" в лог смотрим в докладе Рея или статье Боба (ссылки выше). Естественно, туда можно посылать строки, переменные, объекты, исключения + возможность категоризации посланного. Также можно посылать данные в файловый (=невидимый) лог.

Теперь мы видим, что CodeSite - некое отладочное дополнение к Delphi IDE. Очень удобно, я, например, часто использую эту возможность, чтобы удостовериться в правильном подключении/запросе к БД. Не надо ставить брейкпойнты или выводить в самопальное Memo. Дополнительное окно появляется стабильно хорошо, обладает собственными кнопочками и мега-удобно в использовании. Конечно, раньше мы обходились и Delphi IDE для отладки, но в случае с мобильностью это - отличное решение. Сейчас мы научим наш мобильный клиент "откидывать" инфу на DataSnap-сервер в цивилизованном виде.

Но посылать "отладочные строки" - это же не наш метод! Возьмём сразу быню за ... рога. Мы воспользуемся:

  • возможностью DataSnap автоматически (!) посылать объекты (!) от клиента к серверу;

  • возможностью CodeSite автоматически (!) принимать пользовательские объекты (!) и визуализировать их в "живом окне логгера".


Добавим в проект сервера новый модуль (MyObjectClass.pas), куда занесём немудрящий текст:



Листинг текста не привожу по причине его тривиальности и нежелания навязывать свои вкусы в плане именования.

После чего немного переделаем отклик на кнопку:
uses CodeSiteLogging, MyObjectClass;

procedure TForm1.Button1Click(Sender: TObject);
var
ref : TMyClass;
begin
// CodeSite.Send('hello in CodeSite live logger');
ref := TMyClass.Create;
ref.A := 12;
ref.B := 34.56;
CodeSite.Send('local object', ref);
ref.Free;
//see ARM Compiler language differences
// ... and conditional compilation
end;

Для особо эстетствующих в плане языковых кунштюков - чтим статью Марко Канту по особенностям мобильного компилятора. Как кто кому "фри" вызывает и различия в сборках Win32/ARM.

Теперь же запущенное приложение при нажатии его кнопки добавляет "объект" в окно CodeSite:



Заметьте, что "посланный в CodeSite" объект давно уже убился, но в "живом окне" он будет жить столько, сколько мы хотим на него смотреть. Снимок объекта надолго переживает своего реального брата.

Экспонирование пользовательского метода

Краткое содержание предыдущей серии:

  • заходим по мнемоническуому правилу в серверном проекте в модуль ServerMethodsUnit1.pas. Ищем класс (он там один) и добавляем наш новый метод:




Сам метод будет выглядеть так (прикалываемся от сигнатуры):
uses System.StrUtils, Unit1, CodeSiteLogging;

//...

function TServerMethods1.LogObject(Value: TObject): string;
begin
CodeSite.Send('remote object', Value);
end;

В чём прикол? Только в том, что данный метод будет "выставлен наружу" по факту его вхождения в нужный класс нужного модуля. А может ли "внешний" метод так впрямую принимать ссыль на TObject (да ещё и реально-полиморфную)? Забудьте COM, это - DataSnap. Такие трюки здесь проходят (обратите внимание, на связках Delphi-Client/C++Builder Server и наоборот тоже).

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



Посылка клиентского объекта DataSnap серверу

Для реализации такой схемы нужно в проект DataSnap-клиента добавить описание класса. Правой кнопкой на проекте клиента - Add... - выбираем файл MyObjectClass.pas:



Теперь данный модуль MyObjectClass.pas будет использоваться и в клиентском проекте, и в серверном:



Вот для особо придирчивых (ко мне?) нужно сделать небольшое прагрматическое отступление. Проделанное выше означает, что при создании клиентского приложения должны быть доступны исходники серверного. И наоборот. При создании серверного приложения (в нашем случае так более актуально, т.к. сервер обслуживает логгирующие потребности клиента. Но "чистоты ради" (использование данного шаблона как образца для архитектурного построения многозвенной системы) нужно заметить, что клиент знает серверную реализацию. COM-подобные механизмы как раз и вводят идею "незнания" клиента реализации сервера, а клиенту сообщается только интерфейсы объектов, лишенные реализации. Это нужно для языковой независимости реализаций клиента и сервера, а также решения ситуации, когда "старый клиент" работает с "новым сервером". В предположении, что мы полностью контролируем исходные коды проектов и клиента, и сервера данный подход выглядит вполне разумно.

Доделываем клиентское приложение до передачи объекта. Но сначала поможем клиентскому проекту узнать, что в сервере появился новый метод. Заходим в клиентский проект и... "перегенерируем клиентские исходники" - серверное приложение должно быть запущенным на исполнение, а ip-адрес должен быть валидным:



Это мы проверили "HostName" свойства "Params" компонента SQLConnection1 в ClientModuleUnit1.

Теперь делаем "перегенерацию" методов (внимание, в предыдущих версиях Delphi пункт назывался "proxy", но я полагаюсь на использование XE5):



Теперь клиент точно знает, какие методы экспонированы сервером. Для тех, кто читал курсивный текст выше. Заметьте, что не было в данном месте "передачи исходников" от серверного проекта клиентскому. Т.е. что касается штатной работы DataSnap, разделение на "декларацию" и "реализацию" соблюдается строго. А как это? Да так - DataSnap (из Delphi IDE) "выковыривает" экспонированные методы и генерит клиентский класс (Generate DataSnap client classes, как показано на картинке выше). И на картинке ниже:



Теперь можно клиентом вызывать серверный метод, передавая ссылку на TObject.

Добавляем третью кнопку:



И код вызова серверного метода:
uses ClientModuleUnit1, MyObjectClass;

//...

procedure TForm2.Button3Click(Sender: TObject);
var
MyObject : TMyClass;
begin
MyObject := TMyClass.Create;
MyObject.A := 777;
MyObject.B := 3.1415926;
ClientModule1.ServerMethods1Client.LogObject(MyObject);
end;

Здесь я прошу не придираться к коду. Естественно, локальный объект посылать в сервер смысла большого нет (и думать про создание/уничтожение). В реальной жизни бизнес-объект (скорее всего) будет элементом коллекции, поэтому его управление его временем существование будет частью ответственности более высокоуровневого прикладного кода. Если же нам нужно передать на сервер "просто значение" отладочного характера, то используем перегруженные методы Send сервиса CodeSite.



Только что мы послали объект по ссылке TObject из клиента в сервер DataSnap, где далее мы его по ссылке же TObject пере-направили в CodeSite:
function TServerMethods1.LogObject(Value: TObject): string;
begin
CodeSite.Send('remote object', Value);
end;

Работа "по воздуху"

Пересобранные мобильные клиенты под iOS и Android показали работоспособность:



Мобильные планшеты полностью, они легко отчуждаются от провода. И даже из другой комнаты работают (нужно бы сделать панорамную фотку).

Магия RTTI

Смотрим внимательно, где здесь волшебство.

  • Клиент передаёт объект "по ссылке типа TObject", т.е. метод LogObject может считаться универсальным для клиента.

  • Сервер получает объект "по ссылке типа TObject", т.е. метода LogObject может считаться универсальным для сервера.

  • CodeSite также получает ссылку типа TObject и работает универсально со стороны сервиса логгирования/журналирования.


Единственный "накладной расход" - чтобы RTTI была добавлена в сервер для класса передаваемого объекта, нужно выполнить инстанцирование. Объекты же не передаются "по ссылке" - так выглядит вызов метода из кода Delphi. Вместо этого объекты пакуются в JSON и передаются на сервер. Там - "распаковываются". На эту тему вспоминаем нужную лабу, а также читаем отличный блог, который ведёт Daniele Teti со статьями по сабжу. Там много интересного по части "ручной запаковки объектов в JSON и последующей распаковки".

Заключение

При помощи необременительного добавления к DataSnap-серверу всего одного метода можно легко решать задачу отладки клиентский мобильных приложений для iOS и Android.
Tags: public


Comments

  • Mikel K29672
    Mikel K29672 Thursday, 12 January 2017

    whether there is any way to make datasnap, but that the client could work through mobile data (lte), but isnt in one network?

  • Please login first in order for you to submit comments
  • Page :
  • 1

Check out more tips and tricks in this development video: