Урок 100. Service. IntentService. Foreground. Автозагрузка сервиса
В этом уроке рассмотрим еще несколько полезных вещей про сервисы. Выносить каждую из них в отдельный урок я не стал, вполне можно в одном все рассмотреть. Проекты здесь тоже создавать не будем, чтобы урок не получился слишком громоздким. Я просто приведу некоторые куски кода и скрины для наглядности своих рассуждений. А если у вас будет желание, вы по этим наработкам сами можете создать проекты-примеры.
IntentService
Это подкласс обычного Service. Он используется, если вам в сервисе надо выполнять какие-то тяжелые задачи, и вы не хотите сами возиться с асинхронностью. Принцип работы этого вида сервиса прост. Он создает новый поток для своей работы. Затем берет все Intent пришедшие ему в onStartCommand и отправляет их на обработку в этот поток. Как именно обрабатываются Intent – зависит от нас, т.к. мы сами кодим это в методе onHandleIntent.
Т.е. приложение сыпет в сервис вызовами startService, в которых передает Intent-ы. IntentService принимает эти вызовы в onStartCommand, берет Intent-ы и отправляет их в очередь на обработку. И далее они поочередно обрабатываются в отдельном потоке методом onHandleIntent. Когда последний Intent из очереди обработан, сервис сам завершает свою работу.
В приложении делаем три вызова:
startService(intent.putExtra("time", 3).putExtra("label", "Call 1") ); startService(intent.putExtra("time", 1).putExtra("label", "Call 2") ); startService(intent.putExtra("time", 4).putExtra("label", "Call 3") );
Где time – это время паузы, которую будем делать в сервисе, а label – просто метка, чтобы отличать вызовы.
public class MyService extends IntentService < final String LOG_TAG = "myLogs"; public MyService() < super("myname"); >public void onCreate() < super.onCreate(); Log.d(LOG_TAG, "onCreate"); >@Override protected void onHandleIntent(Intent intent) < int tm = intent.getIntExtra("time", 0); String label = intent.getStringExtra("label"); Log.d(LOG_TAG, "onHandleIntent start " + label); try < TimeUnit.SECONDS.sleep(tm); >catch (InterruptedException e) < e.printStackTrace(); >Log.d(LOG_TAG, "onHandleIntent end " + label); > public void onDestroy() < super.onDestroy(); Log.d(LOG_TAG, "onDestroy"); >>
Здесь необходим конструктор, в котором вызываем конструктор супер-класса и указываем какое-нить имя. Оно будет использовано для наименования потока.
В методе onHandleIntent кодим обработку Intent-ов. Достаем из них time и label, запускаем паузу на time секунд и выводим в лог label в начале и в конце.
В итоге, при запуске в логах видим:
11:07:37.880: D/myLogs(4137): onCreate
11:07:37.880: D/myLogs(4137): onHandleIntent start Call 1
11:07:40.880: D/myLogs(4137): onHandleIntent end Call 1
11:07:40.880: D/myLogs(4137): onHandleIntent start Call 2
11:07:41.880: D/myLogs(4137): onHandleIntent end Call 2
11:07:41.880: D/myLogs(4137): onHandleIntent start Call 3
11:07:45.890: D/myLogs(4137): onHandleIntent end Call 3
11:07:45.890: D/myLogs(4137): onDestroy
Сервис создался, вызовы выполнились по очереди и сервис завершил работу. От нас понадобилось только накодить обработку.
Foreground
Вы можете сказать системе, что ваш сервис очень важен для пользователя и его нельзя грохать при нехватке памяти. Это актуально, например, для музыкального плеера. В статус-бар при этом будет помещено уведомление.
На вход он принимает те же параметры, что и NotificationManager.notify – ID и Notification.
Т.е. вы создаете уведомление, назначаете ему ID и передаете это в startForeground. Сервис переходит в режим IDDQD :), а в статус-баре появилось уведомление.
Оно появилось в разделе для постоянных уведомлений (Ongoing).
Метод stopForeground (boolean removeNotification) — возвращает сервису способность быть убитым системой в случае острой нехватки памяти. А на вход он принимает boolean-значение – удалять уведомление из статус-бара или нет.
Уведомление также пропадет, когда сервис будет остановлен.
Эти методы работают, начиная с Android 2.0. Пример реализации для более ранних версий есть в хелпе.
Напомню, что уведомления мы научились создавать на прошлом уроке.
Автозагрузка
Сервисы для получения погоды или почты имеет смысл помещать в автозагрузку. Для этого нам надо создать BroadcastReceiver, настроить его IntentFilter на Action = android.intent.action.BOOT_COMPLETED, и добавить права android.permission.RECEIVE_BOOT_COMPLETED. Этот BroadcastReceiver будет вызван системой при старте системы и в нем мы кодим запуск сервиса.
Допустим, есть проект с сервисом MyService.
Создаем в проекте класс MyBroadReceiv
public class MyBroadReceiv extends BroadcastReceiver < final String LOG_TAG = "myLogs"; public void onReceive(Context context, Intent intent) < Log.d(LOG_TAG, "onReceive " + intent.getAction()); context.startService(new Intent(context, MyService.class)); >>
В манифесте добавляем его как Receiver и настраиваем фильтр
Добавляем права на получение сообщения о загрузке
Инсталлим проект на AVD. Закрываем AVD. Запускаем через меню в Eclipse: Window > AVD Manager. Находим там наш эмулятор и запускаем вручную.
Когда он запустился, смотрим логи
onReceive android.intent.action.BOOT_COMPLETED
MyService onCreate
MyService onStartCommand
Сработал BroadcastReceiver и запустил сервис.
Если после запуска AVD логи не отображаются, то откройте DDMS и во вкладке Devices явно выберите ваш AVD.
P.S. Я уже писал об этом, но напишу еще раз. Последующие уроки будут выходить по более свободному графику. Следите за обновлениями.
На следующем уроке:
— создаем свой ContentProvider
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
Зачем нужен context в android?
Наткнулся в коде, при создании интента передается объект context . Зачем он нужен, не понимаю ? Если можно, максимально просто.
Отслеживать
13.7k 12 12 золотых знаков 43 43 серебряных знака 75 75 бронзовых знаков
задан 22 фев 2016 в 6:31
536 1 1 золотой знак 6 6 серебряных знаков 16 16 бронзовых знаков
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Context – это объект, который предоставляет доступ к базовым функциям приложения: доступ к ресурсам, к файловой системе, вызов активности и т.д. Activity является подклассом Context , поэтому в коде мы можем использовать её как ИмяАктивности.this (напр. MainActivity.this ), или укороченную запись this . Классы Service , Application и др. также работают с контекстом.
Доступ к контексту можно получить разными способами. Существуют такие методы как getApplicationContext() , getContext() , getBaseContext() или this , который упоминался выше, если используется в активности.
На первых порах не обязательно понимать, зачем он нужен. Достаточно помнить о методах, которые позволяют получить контекст и использовать их в случае необходимости, когда какой-нибудь метод или конструктор будет требовать объект Context в своих параметрах.
В свою очередь Context имеет свои методы, позволяющие получать доступ к ресурсам и другим объектам.
getAssets() getResources() getPackageManager() getString() getSharedPrefsFile()
Context – контекст в android – что это, как получить и зачем использовать
Контекст (Context) – это базовый абстрактный класс, реализация которого обеспечивается системой Android. Этот класс имеет методы для доступа к специфичным для конкретного приложения ресурсам и классам и служит для выполнения операций на уровне приложения, таких, как запуск активностей, отправка широковещательных сообщений, получение намерений и прочее. От класса Context наследуются такие крупные и важные классы, как Application, Activity и Service, поэтому все его методы доступны из этих классов.
Методы получения контекста и их различие
Получить контекст внутри кода можно одним из следующих методов:
- getBaseContext(получить ссылку на базовый контекст)
- getApplicationContext(получить ссылку на объект приложения)
- getContext (внутри активности или сервиса получить ссылку на этот объект)
- this(то же, что и getContext)
- MainActivity.this (внутри вложенного класса или метода получить ссылку на объект MainActivity)
- getActivity(внутри фрагмента получить ссылку на объект родительской активности)
Все эти способы дадут нам возможность получить ссылку на объект, содержащий методы класса Context.
Как было сказано выше, контекст является базовым классом для классов Application, Activity и Service, а значит его методы входят в их состав. Именно поэтому для передачи контекста в качестве параметра можно использовать как ссылку на сам контекст (getBaseContext), так и ссылки на наследуемые классы (getApplicationContext, getContext, this, MainActivity.this, getActivity).
Но тут важно понимать, что время жизни этих ссылок будет разное. Ссылка на переданный объект будет работать, пока будет жить этот объект. Поэтому в качестве контекста важно передать такую ссылку, которая будет рабочей на всём протяжении работы вызываемого метода.
Например, если вызвать сообщение с помощью Toast, используя разные context, то:
Сообщение умрёт вместе с активностью:
Toast.makeText(this, “Text”, Toast.LENGTH_SHORT).show();Сообщение умрёт вместе с приложением:
Toast.makeText(getApplicationContext(), “Text “, Toast.LENGTH_SHORT).show();Будет видно даже после завершения приложения:
Toast.makeText(getBaseContext(), “Text “, Toast.LENGTH_SHORT).show();
То есть, мы видим, что хотя контекст одинаков у разных объектов, сами эти объекты могут жить разное время.
Контекст можно представить, как набор функций для работы на уровне приложения, вошедший в состав таких крупных классов, как Application, разных видов Activity, Service.
Контекст (Context) – это базовый абстрактный класс, реализация которого обеспечивается системой Android. Этот класс имеет методы для доступа к специфичным для конкретного приложения ресурсам и классам и служит для выполнения операций на уровне приложения, таких, как запуск активностей, отправка широковещательных сообщений, получение намерений и прочее. От класса Context наследуются такие крупные и важные классы, как Application, Activity и Service, поэтому все его методы доступны из этих классов. Источник
Методы получения контекста и их различие
Получить контекст внутри кода можно одним из следующих методов:
- getBaseContext(получить ссылку на базовый контекст)
- getApplicationContext(получить ссылку на объект приложения)
- и(внутри активности или сервиса получить ссылку на этот объект)
- this(то же, что и getContext)
- MainActivity.this (внутри вложенного класса или метода получить ссылку на объект MainActivity)
- getActivity(внутри фрагмента получить ссылку на объект родительской активности)
Все эти способы дадут нам возможность получить ссылку на объект, содержащий методы класса Context.
Как было сказано выше, контекст является базовым классом для классов Application, Activity и Service, а значит его методы входят в их состав. Именно поэтому для передачи контекста в качестве параметра можно использовать как ссылку на сам контекст (getBaseContext), так и ссылки на наследуемые классы (getApplicationContext, getContext, this, MainActivity.this, getActivity).
Но тут важно понимать, что время жизни этих ссылок будет разное. Ссылка на переданный объект будет работать, пока будет жить этот объект. Поэтому в качестве контекста важно передать такую ссылку, которая будет рабочей на всём протяжении работы вызываемого метода.
Например, если вызвать сообщение с помощью Toast, используя разные context, то:
Сообщение умрёт вместе с активностью:
Toast.makeText(this, “Text”, Toast.LENGTH_SHORT).show();
Сообщение умрёт вместе с приложением:
Toast.makeText(getApplicationContext(), “Text “, Toast.LENGTH_SHORT).show();
Будет видно даже после завершения приложения:
Toast.makeText(getBaseContext(), “Text “, Toast.LENGTH_SHORT).show();
То есть, мы видим, что хотя контекст одинаков у разных объектов, сами эти объекты могут жить разное время.
Контекст можно представить, как набор функций для работы на уровне приложения, вошедший в состав таких крупных классов, как Application, разных видов Activity, Service.
Вам також може сподобатися

Уроки по android разработке на Java 0 1 611
Урок о том, как в TextView применить тень для текста.

Продвинутые курсы по разработке мобильных приложений на Android 116 15 464
Продвинутый курс “Разработка приложения для сайта” В этом Продвинутом курсе вы узнаете, как создать

Уроки по android разработке на Java 78 405
Приветствуем всех участников продвинутого курса по созданию приложения Youtube! Прежде всего хотим вас всех поблагодарить

Уроки создания андроид-приложений на Kotlin 7 80 808
Продолжаем изучать основы разработки приложений с использованием языка Kotlin. Это уроки по основам разработки,

Уроки по android разработке на Java 0 381
[:ru]Видеоурок о том, как сделать простое приложение для Android, измеряющее силу магнитного поля Земли.
Создание системных сервисов в Android
Игорь Марков, Игорь Починок, НИВЦ МГУ, Auriga Inc.
При разработке собственного аппаратного обеспечения для работы с ОС Android, возникает вопрос, как управлять этим обеспечением из обычных приложений. В предлагаемой статье пошагово демонстрируется процесс создания системного сервиса, позволяющий приложениям Android управлять подключённым оборудованием.
Допустим, мы хотим создать системный сервис, управляющий десятью светодиодами, подключёнными к основной плате устройства. Аппаратная конфигурация состоит из дополнительного микроконтроллера, который управляет светодиодами. Управление самим микроконтроллером осуществляется по шине SPI по заданному протоколу.
Оставим за рамками статьи аппаратную реализацию устройства, управляющего яркостью светодиодов. Достаточно сказать, что оно ожидает 11 байт по шине SPI, первый из которых – фиксированное значение 0xF0, остальные же – значения яркости 10 светодиодов в диапазоне 0 (не горит) до 100 (максимальная яркость).

Блок-схема программного управления светодиодами в ОС Android
Запрос на установку яркости проходит следующий путь (см. Рисунок):
1. Приложение вызывает метод setLights Java-объекта LedLightsManager.
2. Код этого метода вызывает «связанный» механизмом Binder метод setLights системного сервиса LedLightsService. Этот сервис находится в контексте процесса SystemServer, а не процесса приложения.
3. Код этого метода вызывает метод nativeSetLights, написанный на языке C++.
4. Этот метод, в свою очередь, вызывает функцию liblights_write, находящуюся в модуле HAL (динамически подключаемой библиотеке).
5. В свою очередь, эта функция совершает операцию ioctl над файлом «/dev/spidev0.0».
Эта операция выполняется в коде драйвера spidev, который отправляет данные по шине SPI в микроконтроллер, управляющий светодиодами.
Итак, пошаговая схема действий:
1. Создаём модуль HAL.
Модуль HAL (Hardware Abstraction Layer) – это «драйвер» устройства, работающий в пространстве пользователя (в отличие от драйверов, работающих в пространстве ядра Linux, к которым, как правило, обращается модуль HAL для работы с аппаратной частью).
Код модуля должен располагаться в каталоге device///ledlights. Здесь «производитель» и «устройство» заменяем на конкретные названия производителя и устройства, для которого мы создаём модуль HAL. Например, если бы мы создавали модуль для устройства Nexus 5X, то путь выглядел бы как device/lge/bullhead/ledlights.
В рассматриваемом случае весь код модуля HAL находится в одном файле ledlights.c и выглядит следующим образом:
static int spi_fd;
#define LIGHTS_NUM 10
static int liblights_write(char *buffer)
char cmd[LIGHTS_NUM + 1];
memcpy(cmd + 1, buffer, LIGHTS_NUM);
return ioctl(spi_fd, SPI_IOC_MESSAGE(1), &cmd);
static int ledlights_open(const hw_module_t* module, const char* id __unused, hw_device_t** device __unused)
ledlights_device_t *ledlights_dev = (ledlights_device_t*)malloc(sizeof(ledlights_device_t));
__android_log_print(ANDROID_LOG_DEBUG, «LEDLIGHTS», «HAL module opened»);
ledlights_dev->common.module = (hw_module_t *) module;
*device = (hw_device_t *) ledlights_dev;
spi_fd = open(«/dev/spidev0.0», O_RDWR);
return spi_fd >= 0;
static struct hw_module_methods_t ledlights_module_methods =
struct hw_module_t HAL_MODULE_INFO_SYM =
.name = «LED Lights HAL»,
.author = «Your Name Here»,
Основной рабочей функцией является ledligts_write, которая отправляет команду из 11 байт по шине SPI на устройство управления яркостью светодиодов.
Функция ledlighs_open инициализирует модуль. Она открывает символьное устройство «/dev/spidev0.0», которое представляет собой интерфейс к драйверу SPI «spidev», работающему на уровне ядра. Также эта функция инициализирует структуру ledlights_dev, заполняя, в частности, указатели на операции, которые предоставляет данный модуль. В нашем случае это одна функция ledlights_write.
Хотим обратить внимание на структуру HAL_MODULE_INFO_SYM. Она определяет идентификатор модуля HAL и указатель на ledlights_module_methods, которая, в свою очередь, содержит указатель на функцию ledlights_open. Эта структура описывает модуль HAL. Она используется при поиске модуля и для его инициализации.
Файл ledlights.c компилируется в динамически подключаемую библиотеку, модуль HAL.
Для этого необходимо добавить файл сборки Android.mk в device///ledlights:
LOCAL_PATH := $(call my-dir)
Описание же структуры ledlights_device_t и констант LEDLIGHTS_HARDWARE_MODULE_ID и LEDLIGHTS_API_VERSION определяются в файле «hardware/libhardware/include/hardware/ledlights.h»:
#define LEDLIGHTS_HARDWARE_MODULE_ID «ledlights»
#define LEDLIGHTS_API_VERSION HARDWARE_MODULE_API_VERSION(1,0)
struct hw_device_t common;
int (*write)(char *buffer); // buffer is 10 bytes
typedef struct ledlights_device ledlights_device_t;
Данный заголовочный файл используется также и в JNI-части системного сервиса, позволяющим управлять светодиодами.
2. Создаём системный сервис.
Этот сервис состоит из двух частей: части JNI(Java Native Interface), которая расположена в файле «com_android_server_LedLightsService.cpp» в каталоге «frameworks/base/services/core/jni», и части на языке Java.
Основные функции JNI-части:
static ledlights_device_t * device;
static jint initHAL(JNIEnv *env, object clazz)
int res = hw_get_module(LEDLIGHTS_HARDWARE_MODULE_ID, (const hw_module_t**)&module);
res = module->methods->open(module, LEDLIGHTS_HARDWARE_MODULE_ID, (hw_device_t**)&device);
static jint nativeSetLeds(JNIEnv *env, jobject jobj, jbyteArray jarr)
jbyte *buf = env->GetByteArrayElements(jarr, NULL);
res = device->write((char *)buf);
env->ReleaseByteArrayElements(jarr, buf, JNI_ABORT);
Эти функции будут использованы из кода сервиса, написанного на языке Java.
Код сервиса frameworks/base/services/core/com/android/LedLights.java:
public class LedLightsService extends ILedLightsService.Stub
private native int nativeSetLeds(byte[] leds);
public boolean setLights(byte[] leds)
if (leds.length != 10)
return nativeSetLeds(leds) >= 0;
Это код системного сервиса, выполняющегося в контексте процесса SystemServer. Данный процесс включает в себя десятки других системных сервисов, таких как сервис местоположения, ввода, управления WiFi, телефонией, питанием, и т. п.
Регистрация нашего нового сервиса осуществляется добавлением следующих строк в метод startOtherServices в файле «frameworks/base/services/core/com/android/server/SystemServer.java»:
Slog.i(TAG, «LedLights Service»);
> catch (Throwable t )
reportWtf(«starting LedLightsService», t);
Чтобы к сервису можно было обращаться из других процессов (приложений), создадим интерфейс к сервису в новом файле «frameworks/base/core/android/hardware/ILedLightsService.aidl»:
oneway boolean setLights(in byte[] leds);
Этот интерфейс написан на языке Android Interface Definition Language и описывает единственный доступный метод сервиса, setLights.
Создадим также идентификатор для поиска системного сервиса. Для этого добавим в файл «frameworks/base/core/android/content/Context.java» строку:
public final String LED_SERVICE = «led_service»;
3. Создаём вспомогательный класс управления сервисом.
В принципе, этого уже достаточно для того, чтобы нашим сервисом можно было пользоваться из других приложений. Но для удобства создадим вспомогательный класс LedLightsManager, который облегчит доступ к сервису.
public class LedLightsManager
private static LedLightsManager sInstance;
private static ILedLightsService sServiceInstance;
public static LedLightsManager getInstance()
if ( sInstance == null )
if (sServiceInstance != null)
sInstance = new LedLightsManager();
public boolean setLights(byte[] leds)
> catch (RemoteException e)
Класс расположен в файле «frameworks/base/core/android/app/LedLightsManager.java».
Стандартный способ в ОС Android для получения объекта для доступа к системному сервису из обычного приложения, это вызов Context.getSystemService();
Для того чтобы обеспечить возможность получать наш объект класса LedLightsManager таким образом, добавим следующие строки в секцию static в файле «frameworks/base/core/android/app/SystemServiceRegistry.java»:
public LedLightsManager createService(ContextImpl ctx)
4. Собираем прошивку.
Подготавливаем проект к сборке следующей последовательностью команд:
make -j40 update-api
После этого, соберём проект AOSP командой «make -j40». Обновим прошивку устройства одним из двух методов:
1. Перегрузим устройство в режим fastboot командой «adb reboot bootloader». Запишем собранный образ system.img в раздел system: «fastboot flash system system.img; fastboot reboot».
2. Либо обновим содержимое раздела system командами «adb root; adb shell stop; adb remount; adb sync; adb shell start».
Так как появились изменения в системном интерфейсе (API), то соберём Android SDK командой «lunch sdk-eng; make -j40 sdk». При использовании собранного таким образом SDK будут доступны константа Context.LEDLIGHTS_SERVICE и класс LedLightsManager.
5. Создаём приложение.
После обновления прошивки, светодиодами можно управлять из любого приложения. Достаточно использовать следующий код в любом классе, производном от Activity или Service:
LedLightsManager manager = getSystemService(LEDLIGHTS_SERVICE);