Урок 95

- Отримуємо з сервісу результат за допомогою PendingIntent

У минулих уроках ми стартували сервіси, передавали їм дані, але нічого не отримували назад у Activity, що викликає. Але це можливо, і є кілька способів. У цьому уроці розглянемо PendingIntent.

Я особливо не вдаватимуся до пояснення, що таке PendingIntent і що він уміє - урок не про нього. Я покажу, як за його допомогою можна в Activity отримувати результати роботи сервісу.

- у Activity створюємо PendingIntent за допомогою методу createPendingResult

- кладемо цей PendingIntent у звичайний Intent, який використовуємо для старту сервісу та викликаємо startService

- у сервісі витягуємо PendingIntent з отриманого в методі onStartCommand об'єкта Intent

- коли нам необхідно передати результати роботи із сервісу в Activity, викликаємо метод send для об'єкту PendingIntent

- ці результати з сервісу ловимо в Activity у методі onActivityResult

Тобто. фішка PendingIntent тут у тому, що він містить якийсь зв'язок з Activity (у якому він був створений) і, коли викликається метод send, він йде в це Activity і несе дані, якщо потрібно. Загалом такий собі поштовий голуб, який точно знає, як йому повернутися додому.

Схема загалом нескладна. Спробуємо намалювати приклад із нею. У нас буде додаток, який надсилатиме в сервіс на виконання три завдання. А сервіс інформуватиме, коли він почав кожне завдання виконувати, коли закінчив і з яким результатом. Все це виводитимемо на екран Activity.

До речі, щоб легше було сприймати все це, зауважу, що алгоритм дуже схожий на роботу startActivityForResult. Тільки там ми взаємодіємо не із сервісом, а з Activity. Якщо забули, перегляньте уроки 29 і 30. Сприйматипоточний матеріал буде набагато легшим.

Project name : P0951_ServiceBackPendingIntentBuild Target : Android 2.3.3Application name : ServiceBackPendingIntentPackage name : ru.startandroid.develop.p0951servicebackpendingintentCreate Activity : MainActivity

Додамо вstrings.xmlрядки:

Три TextView, в які виводитимемо інфу, що надходить із сервісу. І кнопку старту сервісу.

Створюємо клас для сервісу –MyService.java. І пропишемо його у маніфесті. Поки що в ньому нічого не кодимо.

УonCreateми бачимо TextView і присвоюємо їм початковий текст. Для кожного завдання свій TextView.

УonClickStart​​ми створюємо PendingIntent методом createPendingResult. На вхід до методу передаємо тільки код запиту – можна вважати це ідентифікатором. За цим кодом ми потім визначатимемо, яке саме завдання повернуло відповідь із сервісу. Два інші параметри – це Intent та прапори. Нам вони зараз не потрібні, передаємо відповідно null і 0. (З'ясувалося, що в 4-й версії Андроїд не прокочує використання null замість Intent. Тому можна створити порожній Intent та використовувати його.)

Далі створюємо Intent для виклику сервісу MyService, поміщаємо туди параметр часу (який будемо використовувати для паузи у сервісі) та PendingIntent. Після чого відправляємо це все в сервіс.

Аналогічні дії робимо для Task2 та Task3.

УonActivityResultперевіряємо, який тип повідомлення надійшов із сервісу.

Якщо про те, що завдання розпочала роботу (STATUS_START), то визначаємо, яке саме завдання, і пишемо про це у відповідний TextView.

А якщо про те, що завдання завершено (STATUS_FINISH), то читаємо з Intent-а результат виконання, визначаємо, яке саме завдання, тапишемо інфу про це у відповідний TextView.

Всю інформацію про повідомлення, що надходять, пишемо в балку.

Як ви розумієте, коди STATUS_START і STATUS_FINISH, а також результат ми зараз формуватимемо у сервісі.

Кодим сервісMyService.java:

Ми знову використовуємо знайому за минулими уроками схему екзекутора та Runnable.

УonCreateстворюємо екзекутор із двома потоками. Тобто. коли сервіс отримає три завдання, він відразу почне виконувати два з них, а третє чекатиме вільного потоку.

УonStartCommandвитягуємо з Intent-а параметр часу для паузи та PendingIntent. Створюємо MyRun та передаємо йому ці дані. Передаємо MyRun екзекутору на виконання.

MyRunпід час запуску викликає метод send для PendingIntent і передає туди тип повідомлення STATUS_START. Це приходить в метод onActivityResult в Activity і на екрані ми побачимо, як в одному з TextView з'явиться текст, що завдання почало працювати.

Далі ми емулюємо роботу, як завжди, просто поставивши паузу. А після цього створюємо Intent з результатом роботи (просто час * 100) і викликаємо трохи іншу реалізацію методу send. Окрім типу повідомлення (STATUS_FINISH), ми передаємо туди Intent з результатом та вказуємо контекст. Це йде в метод onActivityResult в Activity і на екрані ми побачимо, як в одному з TextView з'явиться текст, що завдання закінчило роботу з певним результатом.

Далі викликаємо метод stop, у якому викликається метод stopSelfResult.

Все зберігаємо та запускаємо програму.

MyRun

Бачимо, що завдання почали працювати, т.к. екзекутор налаштований на два потоки.

завдання

Одна задача завершилася і показала результат, потік звільнився, стартує завдання.

Activity

Ще одне завдання завершилось.

Activity

Activity

Дивимося логи (у вас може бути трохи інша послідовність записів у логах):

MyService onCreate MyService onStartCommand MyRun#1 create MyService onStartCommand MyRun#2 create MyRun#1 start, time = 7 MyService onStartCommand MyRun# 3 create

Сервіс створився та отримав усі три виклики.

requestCode = 1, resultCode = 100

MyRun1 почав працювати і надіслав повідомлення про це: requestCode = TASK1_CODE, resultCode = STATUS_START.

MyRun#2 start, time = 4 requestCode = 2, resultCode = 100

MyRun2 почав працювати і надіслав повідомлення про це: requestCode = TASK2_CODE, resultCode = STATUS_START.

MyRun#2 end, stopSelfResult(2) = false MyRun#3 start, time = 6 requestCode = 2, resultCode = 200 requestCode = 3, resultCode = 100

MyRun2 завершив роботу та надіслав відповідне повідомлення: requestCode = TASK2_CODE, resultCode = STATUS_FINISH. Потік в екзекуторі звільнився і стартує MyRun3. MyRun3 почав працювати і надіслав повідомлення про це: requestCode = TASK3_CODE, resultCode = STATUS_START.

MyRun#1 end, stopSelfResult(1) = false requestCode = 1, resultCode = 200

MyRun1 завершив роботу та надіслав відповідне повідомлення: requestCode = TASK1_CODE, resultCode = STATUS_FINISH.

requestCode = 3, resultCode = 200 MyRun#3 end, stopSelfResult(3) = true

MyRun3 завершив роботу та надіслав відповідне повідомлення: requestCode = TASK3_CODE, resultCode = STATUS_FINISH.

Останній виклик, що надійшов, виконав метод stopSelfResult, сервіс зупиняється.

Спочатку хотів приклад простіше зробити, але подружжя захопилося, і вийшло складніше, але й цікавіше.

Ще раз проговорю,що ми тут поверхово використовували PendingIntent і почали копати його докладно, т.к. урок не про нього. Скоро ми ще раз зустрінемося з цим об'єктом, коли вивчатимемо повідомлення (Notifications).

На наступному уроці:

- отримуємо із сервісу результат за допомогою BroadcastReceiver

Приєднуйтесь до нас уTelegram:

- у каналі StartAndroid публікуються посилання на нові статті із сайту startandroid.ru та цікаві матеріали з хабра, medium.com тощо.

- у чатах вирішуємо питання і проблеми з різних тем: Android, Kotlin, RxJava, Dagger, Тестування

- ну і якщо просто хочеться поговорити з колегами з розробки, тобто чат Флудільня

- новий чат Performance для обговорення проблем продуктивності та для ваших побажань щодо змісту курсу з цієї теми