Всем привет. Как и большенству разработчиков — мне было лень делать сложные изменения от версии к версии Андроида. Первым таким сложным изменением были -«Runtime permissions», которые пришли к нам с 6-м андроидом. Но это уже в прошлом.
Темой данной публикации будут изменения в Android Oreo. Вы можете почитать подробнее здесь. Все статьи которые я находил, с возможным вариантом решения проблем говорили «Просто наследуй сервис от JobIntentService и используй его в enqueueWork()».
Круто, да. Но не все так просто. Если заглянуть в CommandProcessor, то можно увидеть что сервис останавливается сразу после выполнения onHandleWork(). Это происходит потому, что IntentService не предназначем на выполнения сложных работ, он создан для вещей вроде: доставки ивентов, старта прочих сервисов и так далее.
Эти статьи и их решения ничем мне не помогли, так как они просто копия с developer.android.com. По этому я продолжил тестировать и искать простые варианты использования сервисов пока приложение в фоне ( в совместимости с Android O). И я нашел способ.
Используя JobIntentService можно попробовать запустить ваш сервис по старинке, но контекст предоставляемый JobIntentService не подходит для этих целей, но мы всегда можем запросить контекст приложения. Но даже когда у нас будет контекст приложения, при старте сервиса нас остановят ограничения андроида.
Что же дальше? Эти ограничения на фоновое выполнение работают только тогда, когда вы пытаетесь запустить сервис с помошью startService(), если же вы будете использовать bindService, то никаких ошибок вам андроид не выдаст. Но, в случае байнда сервис будет только создан и прикреплен к приложению и нужно это все делать вместе с созданием и реализацией ServiceConnection, по вызову которого необходимо запустить необходимый метод ( onHandleWork() или onHandleIntent() или необходимый метод вашего сервиса).
Проделывая все это я пришел к заключению что это не самый простой способ. Поэтому я написал маленькую и простую библиотеку которая может делать это все за вас. Она так же позволяет использовать фоновые сервисы без их обьявления в манифесте и создания каких-либо классов. Найти ее можно здесь.
И небольшой пример с гита:
Для того чтобы использовать 2 последних варианта — нужно пронаследовать CompatService
Пример можно найти здесь.
И возвращаясь к теме того с чего я начал «Первым таким сложным изменением были -Runtime permissions», можете так же оценить мой вариант решения этой проблемы.
Темой данной публикации будут изменения в Android Oreo. Вы можете почитать подробнее здесь. Все статьи которые я находил, с возможным вариантом решения проблем говорили «Просто наследуй сервис от JobIntentService и используй его в enqueueWork()».
Круто, да. Но не все так просто. Если заглянуть в CommandProcessor, то можно увидеть что сервис останавливается сразу после выполнения onHandleWork(). Это происходит потому, что IntentService не предназначем на выполнения сложных работ, он создан для вещей вроде: доставки ивентов, старта прочих сервисов и так далее.
while ((work = dequeueWork()) != null) {
if (DEBUG) Log.d(TAG, "Processing next work: " + work);
onHandleWork(work.getIntent());
if (DEBUG) Log.d(TAG, "Completing work: " + work);
work.complete();
}
//Work
@Override
public void complete() {
if (DEBUG) Log.d(TAG, "Stopping self: #" + mStartId);
stopSelf(mStartId);
}
Эти статьи и их решения ничем мне не помогли, так как они просто копия с developer.android.com. По этому я продолжил тестировать и искать простые варианты использования сервисов пока приложение в фоне ( в совместимости с Android O). И я нашел способ.
Используя JobIntentService можно попробовать запустить ваш сервис по старинке, но контекст предоставляемый JobIntentService не подходит для этих целей, но мы всегда можем запросить контекст приложения. Но даже когда у нас будет контекст приложения, при старте сервиса нас остановят ограничения андроида.
Что же дальше? Эти ограничения на фоновое выполнение работают только тогда, когда вы пытаетесь запустить сервис с помошью startService(), если же вы будете использовать bindService, то никаких ошибок вам андроид не выдаст. Но, в случае байнда сервис будет только создан и прикреплен к приложению и нужно это все делать вместе с созданием и реализацией ServiceConnection, по вызову которого необходимо запустить необходимый метод ( onHandleWork() или onHandleIntent() или необходимый метод вашего сервиса).
Проделывая все это я пришел к заключению что это не самый простой способ. Поэтому я написал маленькую и простую библиотеку которая может делать это все за вас. Она так же позволяет использовать фоновые сервисы без их обьявления в манифесте и создания каких-либо классов. Найти ее можно здесь.
И небольшой пример с гита:
//No need to create classes and add manifest declarations
ServiceManager.runService(context, () -> {Logg.e(TAG,"Some Action");},true);
//if you have already prepared service you can us it as :
ServiceManager.runService(context, GeofenceTransitionsIntentService.class);
//or if you need to add some data or actions you can use it like :
Intent geo = new Intent(context, GeofenceTransitionsIntentService.class);
geo.setAction(GeofenceTransitionsIntentService.ACTION_REQUEST_LOCATIONS);
ServiceManager.runService(context, geo);
Для того чтобы использовать 2 последних варианта — нужно пронаследовать CompatService
Пример можно найти здесь.
И возвращаясь к теме того с чего я начал «Первым таким сложным изменением были -Runtime permissions», можете так же оценить мой вариант решения этой проблемы.
Комментарии (8)
androidovshchik
28.10.2017 17:13Я всегда наследую
Service
. Спасибо за информацию оbindService
, не знал
Dronchick
30.10.2017 18:32Зачем вы пробуете наследоваться от JobIntentService если вам не нужен HandlerThread? Наследуйтесь от JobService
Vulko
30.10.2017 18:34Уважаемый автор… а вы в курсе что IntentService выполняет задачи в worker thread? И как раз для выполнения сложных работ он отлично подходит… или блокирующих caller thread.
Он просто не предназначен для того чтобы вечно жить в бэкраунде, жрать батарейку и выполнять говнокод.Euzee Автор
30.10.2017 18:40Возможно так и было до прихода JobScheduler спорить не стану, я лично использовал его для Geofencing и по рекомендации доки , в которой и написано «However, in most cases an IntentService is the preferred way to perform simple background operations».
VioletGiraffe
Спасибо за permissionUtil. До сих пор не осилил runtime permissions, но, чувствую, уже пора.
Mr4Mike4
github.com/googlesamples/easypermissions
Тоже хорошая библиотека для runtime permissions
Euzee Автор
Спорить не стану, но основной упор permissionUtil на простоту использования.