понедельник, 25 января 2016 г.

[Android Service] Создание простейшего Сервиса (службы)

Сегодня я начну цикл статей посвящённых созданию сервисов под Android на Delphi. Данные статьи в первую очередь предназначены новичкам. В статье мы попробуем с вами создать простейший Сервис.


Создание сервиса.
  1. Запускаем студию
  2. Жмём File – New - Other
  3. В открывшемся окне выбираем Android Service
  4. В след. окне выбираем Local Service и жмём OK
Студия создаст проект, в Project Manager вы увидите что-то вроде этого:



Давайте дадим проекту более подходящее название, например «MyFirstService».
Теперь сохраним проект в отдельную папку.
Я сделал так: Создал папку «MyFirstServiceProject», в ней создал папку «MyFirstService» и уже в неё сохранил созданный выше сервис.
Внимание!
  • Всегда сохраняйте созданный Сервис в отдельную папку!
  • Никогда не переименовывайте Сервис в «Service»!
Теперь давайте сразу напишем код, необходимый для запуска сервиса.

Дважды щёлкаем в Project Manager по Unit1.pas, перед нами появится форма, переключаемся в режим редактирвоания кода и начинаем писать:
  1. Подключим юнит, позволяющий нам работать с АПИ Андроида, в uses прописываем: Androidapi.JNI.App;
  2. Теперь возвращаемся в режим изменения формы, в Object Inspector выбираем вкладку «Events», там находим событие «OnStartCommand» и дважды щёлкаем по пустому полю рядом.
  3. Студия создаст за нас шаблон функции, нам остаётся только вписать необходимый код.

Код:
Result := TJService.JavaClass.START_STICKY;

Разберём этот код:
В данной строчке мы возвращаем код, позволяющий указать системе, что делать с сервисом, если он был остановлен.

Виды доступных констант:
  • START_STICKY – используется для автоматического перезапуска сервиса (службы), в случае, если система по какой-то причине убила его.
  • START_NOT_STICKY – используется для отмены автоматического перезапуска службы. Подобные сервисы используются для выполнения разовых операций/команд.
  • START_REDELIVER_INTENT – это комбинация предыдущих констант.
Более подробно читайте в официальной справке Service - Service Lifecycle.
Для статьи мы используем первую константу, чтобы показать, что сервис работает и перезапускается при необходимости.
Возвращаемся в среду разработки и сохраняем проект. После чего в Project Manager щёлкаем правой кнопкой мышки по названию проекта (примерно: «libMyFirstService.so») и выбираем «Build».
Создание сервиса завершено, теперь нужно разработать приложение, запускающее этот сервис.

Создание приложения для управления сервисом.
В Project Manager щёлкаем по группе «ProjectGroup1» правой кнопкой, выбираем «Add New Project» и создаём проект «Delphi Projects > Multi-Device Application».
Проект переименуем в «MyFirstServiceApp»
Размещаем на форме кнопку «Start Service» и пишем код для запуска сервиса.
В uses подключаем юнит «System.Android.Service;»

Код кнопки:
TLocalServiceConnection.StartService('MyFirstService');

Теперь остаётся только добавить сервис в приложение и запустить его.

Сейчас наш Project Manager выглядит так:


Щёлкаем правой кнопкой мыши по платформе Android (Android – Android SDK…) и жмём «Add Android Service». Выбираем папку в которой лежит сервис, путь будет примерно такой C:\Users\Infocean\Desktop\...\MyFirstServiceProject\MyFirstService
Жмём Next и Finish. 

Студия сама добавит 3 файла в проект приложения:
C:\Users\Infocean\Desktop\...\MyFirstServiceProject\MyFirstService\Android\Debug\libMyFirstService.so
C:\Users\Infocean\Desktop\...\MyFirstServiceProject\MyFirstService\Android\Debug\MyFirstService.jar
C:\Users\Infocean\Desktop\...\MyFirstServiceProject\MyFirstService\Unit1.pas

На этом всё, компилируем проект на устройстве.

Внимание!
Если вы скачали готовый проект, то обязательно перепроверьте пути в Deployment Manager. Возможна ситуация, когда пути до библиотек будут прописаны мои, т.е. вам необходимо:
в Deployment Manager нажать на кнопку "Revert to Default"
в Project Manager удалить из проекта ".. - MyFirstService - Unit1.pas"
после этого заново добавьте сервис (службу) в приложение.
Далее можно компилировать.


Справка по сервисам - Creating Android Services

Видео

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

  1. Привет.
    MyFirstService тоже компилируется? как отдельное ПО? или оно будет скомпилировано в MyFirstServiceApp

    ОтветитьУдалить
    Ответы
    1. Шаги такие:
      1) MyFirstService - только собирается через Build, без компиляции.
      2) Потом MyFirstService мы добавляем в MyFirstServiceApp
      3) Компилируем MyFirstServiceApp

      Удалить
    2. Спасибо за ответ. После запуска службы, приложение зависает, может его в потоке запускать? и ещё вопрос, что бы служба запускалась при включении ОС, что для этого надо сделать?

      Удалить
    3. Эм... смотрите, что пишется в логах в момент зависания, у меня такого поведения не наблюдается.
      Нужно подписать службу на события системы, через BroadcastReceiver запускать её. У меня пока не вышло реализовать это, но в планах такая заметка/статья есть.
      В скором времени опубликую статью, о том как сделать автозапуск приложения после перезагрузки ОС.

      Удалить
    4. Сейчас решил проверить ещё раз весь проект. Все работает, ничего не виснет.
      Правда обнаружил, что студия сохраняет мой абсолютный путь до службы, для исправления нужно передобавить службу в приложение.

      Если вы скачали готовый проект, то обязательно перепроверьте пути в Deployment Manager. Возможна ситуация, когда пути до библиотек будут прописаны мои, т.е. вам необходимо:
      в Deployment Manager нажать на кнопку "Revert to Default"
      в Project Manager удалить из проекта ".. - MyFirstService - Unit1.pas"
      после этого заново добавьте сервис (службу) в приложение.
      Далее можно компилировать.

      Удалить
    5. Здравствуйте! все работает спасибо, сделал так чтобы каждые 10 мин обновлял данные в MySQL сервере но при закрытии MyFirstServiceApp служба перезапускается и перестает отправлять запросы на сервер можете сказать в чем проблема?

      Удалить
  2. Можно узнать Вашу версию JDK, android API

    ОтветитьУдалить
    Ответы
    1. C:\Program Files\Java\jdk1.8.0_71 или
      C:\Program Files (x86)\Java\jdk1.8.0_71
      Какой прописывать?
      Подозреваю из за этого зависает

      Удалить
    2. Это одно и тоже. Оставьте это C:\Program Files\Java\jdk1.8.0_71 :)
      Сомневаюсь, что зависает из-за этого... Что в логах пишется?

      Удалить
    3. Извиняюсь я новичок в этом, а где логи смотрят?

      Удалить
    4. Логи смотрят в специальной утилите "monitor.bat". Лежит она тут: C:\Users\Public\Documents\Embarcadero\Studio\17.0\PlatformSDKs\android-sdk-windows\tools\monitor.bat

      Удалить
  3. Доброго времени суток!
    Разобрался в чем была соль. При подключении модуля FMX.Types к проекту сервиса, приложение при запуске сервиса зависает. Хотелось бы посмотреть пример, как из сервиса через определённое время идет соединение с Интернетом. То есть использование модулей TTimer, TIndHTTP.
    Модуль System.Notification работает. Есть ли список разрешенных модулей к использованию в сервисе?

    ОтветитьУдалить
    Ответы
    1. Списка такого, вроде не встречал. Но скорее всего в след. статьях по сервисам я буду потихоньку рассматривать подключение наиболее популярных модулей.

      Удалить
    2. Для периодических подключений к интернету, будь то обновление базы или проверка новых сообщений и т.п. лучше использовать разовые авто-запуски службы. Т.е. запустили, проверили, завершили. Это позволит экономить ресурсы всей системы и устройства.

      Удалить
  4. [b]Andrey Efimov[/b], Андрей большое спасибо Вам за огромный труд!
    Подскажите пож-ста пример приложения под Android (заготовка) - = дневник/журнал который ведет пользователь на планшете, с возможностью приклепления к записям в БД фото с камеры.
    и второй вопрос десктоп приложение на которое можно время от времени массово сливать записи с фото. Спасибо большое!

    ОтветитьУдалить
    Ответы
    1. Примера такого не встречал. На первый взгляд, там нет ничего сложного.
      А вот со вторым вопросом сложнее, нужно как-то реализовать синхронизацию, в Андроид есть ограничения. Первое, что приходит в голову, делать синхронизацию в лоб, т.е. если записи и фото(в blob), то можно попробовать вытаскивать файл базы в общедоступную директорию, сливаем файл на комп, а на компе уже подключаемся к нему и синхронизируем с основной базой. Если же в базе хранятся только имена и пути до картинок, то тут можно попробовать просто передавать все картинки, которые упомянуты в базе, ну и саму базу.

      Удалить
    2. Ещё важно определиться с тем, как вы хотите подключаться к компу, по ЮСБ, Wi-Fi, Bluetooth или вообще через Интернет по прямому IP.

      Удалить
    3. Андрей, спасибо за ответ. По первому пункту,
      вроде нет ничего сложного, но такого примера не нашел: либо базы без изображений, либо работа с изображениями без баз ))
      Смотрю этот пример:
      где http://delphifmandroid.blogspot.ru/2014/03/blog-post.html
      У вас изображения хранятся в ресурсах, а в БД ссылки.
      Этот вариант пойдет для моей задачи? Скажем в журнале за раз будет появляться 200-300 изображений, потом пользователь будет сливать базу на ПК.

      По 2-му пункту: как быстрее и проще так и лучше: тогда по USB наверно.

      Удалить
    4. Ну вы же не думаете, что есть примеры на все случаи жизни. Для того среда и создана, чтобы вы писали что-то своё...
      По поводу моей статьи: Вам такой вариант не подойдёт, вам нужно понять свою задачу, тогда вы поймёте какие способы и технологии использовать. 200-300 изображений за раз?! Откуда они будут браться, это будут фотографии с камеры или выкаченные картинки из интернета?
      В любом случае, хранить такое количество картинок в базе, не вариант. Поэтому в базе храните имена и возможно, пути до картинок.

      В общем, вам необходимо сначала правильно сформулировать и описать задачу. Потом уже можно будет примерно определиться с технологиями.

      По подключению, все способы хороши. Для Wi-Fi и Bluetoothможно попробовать задействовать технологию App Tethering, если мне не изменяет память, то помимо текстовых данных, она умеет передавать файлы.

      Удалить
    5. Андрей, сорри за неточности в формулировке.
      За раз я имею ввиду: например человек ушел в поход на неделю, за это время нафотографировал 100-300 фотографий (фотографии поступают только с камеры или cameraroll). Затем добрался до ПК, подключился пусть по USB и слил данные и фото в десктоп приложение.
      По USB это будет быстро. Но простого решения как это сделать по USB не попалось на глаза.
      App Tethering - таких примеров несколько. Но не знаю на сколько оно годится для массовой перекачки фото.

      Удалить
    6. Ага, понял. Ну либо вручную копировать папку с фотками и базой, либо, пока приходит на ум, только использование ADB, если App Tethering вам не подходит.

      Удалить
    7. Андрей, спасибо ) ADB - поищу.
      1) "вручную копировать базу и файлы" - есть такие примеры?
      2) Фотографии хранить в assets?
      У меня вообще нет опыта работы с Андроидом, неохото идти по неправильному пути )

      Удалить
    8. 1) Эм... ну как вы копируете файлы с устройства на комп и наоборот...
      2) Фотографии хранить естественно в галерее или в папке вашего приложения на сд-карте.
      Ну, раз у вас вообще нет опыта работы с Андроидом, то для начала советую, прочитать несколько статей или книжек, по использованию Андроида с позиции пользователя и разработчика.

      Удалить
    9. Андрей, книжки статьи это хорошо )
      Еще несколько вопросов:
      1) У меня Nexus 2013 без сд-карты, есть пример копирования базы и файлов?
      2) Ваша игра Ребусы очень помогла, но очень лениво разрабатывать/отлаживать на андроиде, скажите можно ли к ней добавить конфигурацию чтобы она и под win32 компилилась?

      Удалить
    10. Ну так почитайте... ;)
      По вопросам:
      1) О каких примерах опять идёт речь? Вручную - при подключении устройства к ПК, оно будет отображаться как флешка, значит и копировать можно всё как с флешки. А по коду, я уже говорил, я примеров не встречал. Куда сохранить базу, можно посмотреть и в официальной справке, и в моих же статьях "Deployment Manager или куда ещё можно задеплоить файлы", "Android 4.4 и запись на внешнюю карту памяти...". В общем всё, что необходимо, есть, вам остаётся только почитать про основы пользованием Андроидом и всё, хотя можно и этого не делать, но тогда методом тыка изучать будете.
      2) Да, конечно можно, я не против :). Скорее всего нужно будет прописать путь для базы и т.п., но это реально, ведь приложение написано на FMX.

      Удалить
    11. Андрей, примерно понял, сначала Андроид приложением копируем в раздел SD Card, а оттуда десктопным забираем.
      Добавил платформу, перестала компилироваться андроид версия )) Надо разбираться.
      А как вы разрабатывали и отлаживали приложение? Постоянно запускали на андроид устройстве?

      Удалить
    12. Да, я разрабатываю и отлаживаю сразу на устройстве.

      Удалить
    13. Этот комментарий был удален автором.

      Удалить
  5. Здравствуйте. Интересует следующий вопрос. Например у меня есть сервис который периодически делает http запрос. Вопрос в следующем как мне получить ответ http в основной программе?

    ОтветитьУдалить
    Ответы
    1. Общение между сервисом и приложением можно организовать при помощи интентов, другого способа я пока не знаю.

      Удалить
    2. Большое спасибо буду разбираться если что то получится отпишусь здесь.

      Удалить
  6. Добрый день! Вопрос может быть не в эту тему! Можно ли программно включить GPS-модуль?

    ОтветитьУдалить
  7. сервис работает, круто, спасибо! А как заставить его что то сделать? простейшее showmessage в timer ничего не выполняет...

    ОтветитьУдалить
    Ответы
    1. Про таймер, посмотрите эту тему http://fire-monkey.ru/topic/2275-taymer-v-servise/?do=findComment&comment=16522
      Про showmessage, лучше использовать Toast или Notification.

      Удалить