Delphi (C++Builder) Android Mobile Client DataSnap Server

by Mar 21, 2014

Преамбула

DataSnap – платформа создания многозвенных приложений. Её история достаточно продолжительна, начиная с совсем ранних версий Delphi. Роль и значимость этой технологии/платформы изменялась в течение эволюции Delphi/C++Builder/RAD Studio, но сейчас есть ряд очевиднейших преимуществ от её использования в контексте мобильной разработки.

DataSnap подходит для создания «бэкенда» для мобильного приложения в корпоративном использовании.  Проще говоря, создав первое мобильное Android/iOS приложение, вам захочется обеспечить его «связь с внешним миром», причем не с «совсем уже внешними» сервисами (для этого, вполне вероятно, подойдёт RESTClientLibrary здесь, хотя и про C++Builder, но можно также и тут). Своя же связь (настольного) приложения для большинства «дельфистов» означает работу в режиме клиент/сервер. Для работы в режиме клиент/сервер полезным будет использование связки FireDAC + InterBase (поясняющее видео, там, правда, сначала обзор конкурса – так случайно получилось, а уже про клиент-сервер – начиная с 12-ой минуты. Там про iPhone и C++Builder, но всё работает 100% для Delphi и Android).

Внешний web-сервис REST требует наличия как-минимум такового. Можно писать самому, кто обладает достаточной компетенцией в данном вопросе. Можно пытаться делать всё «классически» через клиент-сервер. Клиент-сервер в качестве технологии передачи данных от мобильного клиента в сервер БД сразу же зажимает разработчика в определенные рамки. Прежде всего, это – занесение данных в базу посредством SQL-запроса. Но для первого приближения хочется простого, милого, доброго и доступного – нажать кнопку на мобильном приложении и увидеть «отработку» в приложении, запущенном на настольном компьютере. Давайте это и сделаем.

DataSnap сервер

Начнём с проекта, которое будет играть роль сервера приложений. Это будет крайне просто, мы создадим «обычное» VCL-приложение. Единственное, что будет отличать его от «классического» – умение «слышать» обращения клиентов извне. В Delphi и C++Builder это будет феноменально просто.

В Delphi/C++Builder подобные вещи делаются интерактивно, визуально и легко (начиная с Delphi XE, когда платформа DataSnap была уже сформирована в практически готовом виде). Мы не будем «преобразовывать» этот просто VCL-desktop проект в нечто, способное реагировать на действия мобильного приложения (хотя это можно, я покажу потом как). Мы создадим новый DataSnap Server на основе VCL Forms Application. Будучи запущенным, он сыграет роль «обычного Windows-приложения на основе VCL» с несколько расширенными функциями.

Запускаем Delphi XE5/C++Builder XE5. Выполним File->New->Other… | <узел DataSnap> DataSnap Server, как показано на картинке:

Дальше пойдут окна мастера создания DataSnap Server, далее будут «правильные» скрин-шоты с небольшими пояснениями:

Мы оставим выбор «VCL Forms Application», хотя по названию двух других пунктов можно догадаться, что они означают. Да, это будет обычным VCL-приложением, которому будут добавлены функции «прослушки» обращений и реакции на обращения мобильных клиентов.

Кнопка «Next >>» явит следующее окно:

Оставим тоже всё как есть, альтернативы с протоколом понятны, а аутентификация есть предмет отдельной работы (например, здесь, также ищете по Pawel Glowacki и DataSnap). Но впоследствии можно «нагрузить» нашу связку и системой аутентификации – изобретать «велосипед» смысла большого нет. Далее есть еще и фильтры шифрования/сжатия для воздействия на передаваемый поток байтов, но это уже потом (давний материал релизов XE-XE3). Нам нужно перейти на следующую ступень (кнопка «Next>>»):

Тестируем порт (211) на предмет незанятости – успешный тест гарантирует «соединяемость» с сервером из клиентского приложения. Нажимаем «Next>>».

Здесь выбираем пункт TDSServerModule как самую «прокаченную» опцию. Можно прибедниться за счёт двух предыдущих пунктов, дабы сэкономить, но смысла большого нет. Выбираем «Finish».

Это – нормальное, обычное приложение, ему можно даже сделать какой-то вменяемый интерфейс, т.е. получить приложение, работающее самостоятельно и самодостаточно, но умеющее также выполнять некие методы, будучи вызванными извне.

Все эти «заморочки» с созданием сервера DataSnap, живущего внутри VCL-приложения, нужны, чтобы «выставить наружу» некий функционал. Технически это выглядит как добавление некоторых «странных» модулей, помимо классических Form1.pas и Form1.dfm за счет отработки мастера DataSnap Wizard (который мы только что использовали). Сохраним всё как есть, но зададим лишь другое имя проекта – DataSnapMobileServer (таким будет и результирующий exe – DataSnapMobileServer.exe). Разбираемся подробно:

Unit1.pas, Unit1.dfm – это «классика», не нуждающаяся в пояснении. Сама IDE (Delphi XE5/C++Builder XE5) по результатам работы с DataSnap Wizard (тем самым четырёхоконным мастером) выдала нам ещё парочку сладких модулей ServerContainerUnit1 и ServerMethodsUnit1.

ServerContainerUnit1 в виде визуального контейнера нам интересен будет потом, когда мы будем развивать функционал сервера в платформенной части (жизненный цикл серверных объектов, аутентификация, фильтры шифрования/сжатия и т.д.). Модуль в этом «юните» и есть главный серверный объект. Обратите внимание (в коде), что класс серверного контейнера наследуется от TDataModule, а с ним-то мы уже умеем работать! Серверный модуль – просто некий визуальный контейнер в IDE в design-time, а также определенный объект, создаваемый в runtime. В этом модуле и конфигурируется сервер приложений за счёт добавления на него определенных компонентов. Пока у нас лишь «прожиточный минимум» в виде трёх компонентов, функционал которых приблизительно ясен из названий.

ServerMethodsUnit1 вообще не блещет визуалкой, он – «пустышка». Но именно тут нам предстоит выполнить некое кодирование. Нажмем F12, войдем в редактор кода для данного модуля и увидим класс с двумя тестовыми методами:

TServerMethods1 = class(TDSServerModule)
private
{ Private declarations }
public
{ Public declarations }
function EchoString(Value: string): string
function ReverseString(Value: string): string
end

Запомним это место, скоро мы вернёмся сюда еще раз. Мнемоническое правило по разделению модулей ServerContainerUnit и ServerMethodsUnit таково: «контейнер» для размещения компонентов, «модуль методов» – для программирования методов. Два метода EchoString и ReverseString сгенерированы автоматом как тестовые функции сервера, однако уже доступные «извне».

Перейдём к главной (и единственной) VCL-форме проекта и добавим на неё компонент TMemo:

Если мы теперь попробуем использовать данный проект, то у нас будет VCL-приложение с двумя доступными «извне» методами: EchoString и ReverseString – см. код выше.

Нашей целью является создание DataSnap сервера, способного каким-нибудь интересным образом реагировать на запросы мобильного клиента. Мы добавим наш собственный метод. Мобильное приложение на мобильном устройстве будет пулять строку по WiFi на сервер при помощи этого метода.

Вернёмся в ServerMethodUnit1, найдем методы EchoString и ReverseString, а чуть ниже допишем свой собственный метод по образу и подобию:

function LogString(Value: string): string

Нажмём Ctrl+C, получим сгенерированную «заглушку» реализации, куда допишем свой код:

function TServerMethods1.LogString(Value: string): string
begin
Form1.Memo1.Lines.Add(Value)
end

Если у вас данный код не компилируется, нажмите Alt+F11 и выберите Unit1.pas. Этот модуль попадёт в список uses, поэтому можно будет обращаться к главной форме.

Скомпилируйтесь и запустите приложение кнопкой Run Without Degugging ("зелёный треугольник без жучка"). Фишка в том, чтобы приложение запустилось «без IDE» и спокойно «висело». В смысле, вас не должно беспокоить, что вы запустили и забыли приложение. Так и надо, наш мобильный клиент будет к нему подцепляться сначала из IDE, а потом самостоятельно.

DataSnap Mobile Client

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

DataSnap сервер мы делали так: сразу и приложение, и серверный объект (форма и серенькое «облачко» выше). Клиент мы будем делать в два приёма – сначала сделаем приложение, а потом «внедрим» туда клиентскую часть (зелёное «облачко» на картинке выше).

Встанем на проектную группу в «менеджере проекта», правой мышью, Add New Project:

Затем в окне проектов кликнем на FireMonkey Mobile Application, затем Blank Application:

В менеджере проектов появиться новый проект. Сохраним его, изменив лишь название самого проекта на DataSnapMobileClient. В данном проекте пока нет ничего интересного, разве что по правой мышке добавим Win32 в список TargetPlatforms:

Это – хорошая идея, делать Win32 сборку для мобильного проекта. Отладка и тестирование гораздо эффективней на таком варианте, чем ждать "деплоймента" на железку. Делайте это на Win32 для:

  • разработка и отладка алгоритмов;

  • разработка и отладка бизнес-логики;

  • разработка и отладка работы с данными (локально, клиент-сервер, DataSnap – как в нашем случае, REST – библиотека REST Client Library как и всё перечисленное работают и в "настольных" Win32-сборках) – наш вебинар с Дмитрием Кузьменко как раз и показывает такую работу.

Оставаясь же на данном проект выполним File->New->Other | <узел DataSnap> DataSnap Client Module:

Теперь к существующему мобильному приложению будет добавлен функционал для связи с уже созданным (и запущенным) сервером! Это приложение станет "клиентом" (в нём появится "зелёное облачко"). Осталось всего несколько шагов.

Выбираем Remote server, т.к. клиентское приложение будет запущено (в конце концов) на отдельном устройстве (у меня на планшете Android – Nexus 7).

Выбираем первую опцию, так как у нас именно «изолированное VCL-приложение». Но остальные опции тоже полезно прочитать и запомнить.

При изготовлении сервера мы концентрировали наше внимание на TCP/IP, логично продолжить данную традицию.

Вот тут обратите внимание, что IP-адрес (он же Host name) берётся не «с потолка», а именно тот, на котором висит WiFi вашего компьютера, где запущен DataSnap сервер (не пишите туда localhost даже в шутку, такие шутки чреваты потерями времени и нервными криками "ну где-же коннект?").

Для совсем новичков в данной теме. Компьютеры под Windows 7 способны иметь два соединения одновременно – и «по проводу», и по «вайфай». Не надо выдёргивать сетевой шнур для тестирования работы по WiFi. Не забудьте узнать ip-адрес wifi компьютера и не путайте его «с тем, что по шнурку». Самое немудрящее – консоль cmd и ipconfig. Далее, пожалуйста, подключите к одной и той же сети WiFi и данный компьютер, и мобильной устройство . В офисе бывает несколько сетей, а автор данного текста потерял 30 минут времени, т.к. мобильные устройства могут самостоятельно менять WiFi сеть. И мобила, и десктоп должны быть на одной сети. Бывает, что сеть есть, а пинга нет (ip не пробивается с устройства на устройство). Как говорится, обратитесь к системному администратору. В любом случае, кнопка Test Connection вам в помощь, а если не работает, то самое время сделать перерыв и разобраться с сетью.

На картинке выше я показал, что "повесил всё на одну сеть" и точно знаю ip-адреса устройств.

Вернёмся к проекту:

Я аккуратно позакрывал все закладки с серверными исходниками, там уже все сделано. Напоминаю, что само серверное приложение запущено и остаётся таковым. Закрывать не нужно и нельзя. На минутку сбегаем в модуль ClientClasses1. О чудо! Там показаны сгеренированные на запущенном сервере методы. Еще раз: Delphi/C++Builder IDE накинулась на запущенный сервер при создании DataSnap Client Module и воспроизвела те методы, которые сервер «выставлял наружу»:

TServerMethods1Client = class(TDSAdminClient)

function EchoString(Value: string): string
function ReverseString(Value: string): string
function LogString(Value: string): string
end

Не путайтесь – это не общий исходник с серверным проектом. У сервера методы и их реализации. В клиенте – лишь «заглушки» для вызова этих методов. Да – методы выглядят «одинаково», но это лишь по сигнатурам. Классы их разные, а в сервере они реализованы, а в клиенте – нет. Они просто «отсюда» вызываются. Как? А вот это уже совсем а) не наше дело б) не интересно. Все сделает умный и добрый DataSnap с его транспортом, маршаллингом и т.д.

Нам осталось сделать парочку манипуляций.

Открываем главную форму мобильного приложения, добавляем на неё 2 кнопки:

Под код каждой кнопки подписываем следущее (событие OnClick для каждой кнопки программируется отдельно – а вдруг меня читают совсем «нулёвые»?):

uses ClientModuleUnit1

procedure TForm9.Button1Click(Sender: TObject)
begin
ClientModule1.ServerMethods1Client.LogString('button 1')
end

procedure TForm9.Button2Click(Sender: TObject)
begin
ClientModule1.ServerMethods1Client.LogString('button 2')
end

Не забываем магию Alt+F11 для ClientModuleunit1.pas, что породило первую строку с uses в листинге выше.

Пуско-наладочные работы

Сильно рекомендуется не стартовать мобильную сборку, а собрать проект под Win32 (для этого мы данную платформу и добавляли).

Собираем проект под Win32 и запускаем пока на одной машине. Кликаем – наслаждаемся эффектом. Пока не впечатляет в силу того, что всё происходит на одной машине и совсем без удалённого подключения мобильного устройства.

Дело – за малым. Пересобрать проект под мобильное устройство и еще раз протестировать великолепие грамотно спроектированного и эффективно реализованного функционала.

Я "оторвал" планшет сразу после успешного запуска клиентского приложения от USB-кабеля и "чисто по воздуху" управлял серверным приложением, нажимая кнопки на мобильному устройстве.

Висящий провод символизирует оторванность планшета Nexus 7 с мобильным клиентом от ноутбука, где запущен сервер приложений. Палец символизирует потребность пользователя в работающем приложении, что есть движущая сила разработки любого ПО.

Для тех, у кого получился данный пример, домашнее задание – проработать лабы в «настольном» варианте клиента (очень полезно, поверьте). Потом опять можно продолжить эксперименты с мобильными приложениями.

Пользуясь случаем, передаю привет Владимиру Крапоткину (семинар в Тюмени), который не просто профессионал высокого уровня, но и успешный пользователь DataSnap. Мы здорово пообсуждали его проекты с использованием DataSnap, а также достоинства и недостатки асинхронного вызова клиентских heavy-weight callbacks (еще раз – сделайте  лабы) в реальной жизни. Владимир – спасибо Вам! Такие вот "астральные братья" 🙂