Всем привет. Как и большенству разработчиков — мне было лень делать сложные изменения от версии к версии Андроида. Первым таким сложным изменением были -«Runtime permissions», которые пришли к нам с 6-м андроидом. Но это уже в прошлом.

Темой данной публикации будут изменения в 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)


  1. VioletGiraffe
    28.10.2017 10:27

    Спасибо за permissionUtil. До сих пор не осилил runtime permissions, но, чувствую, уже пора.


    1. Mr4Mike4
      30.10.2017 18:28

      github.com/googlesamples/easypermissions
      Тоже хорошая библиотека для runtime permissions


      1. Euzee Автор
        30.10.2017 18:30

        Спорить не стану, но основной упор permissionUtil на простоту использования.


  1. androidovshchik
    28.10.2017 17:13

    Я всегда наследую Service. Спасибо за информацию о bindService, не знал


  1. Dronchick
    30.10.2017 18:32

    Зачем вы пробуете наследоваться от JobIntentService если вам не нужен HandlerThread? Наследуйтесь от JobService


    1. Euzee Автор
      30.10.2017 18:33

      Для совместимости. JobService «added in API level 21»


  1. Vulko
    30.10.2017 18:34

    Уважаемый автор… а вы в курсе что IntentService выполняет задачи в worker thread? И как раз для выполнения сложных работ он отлично подходит… или блокирующих caller thread.

    Он просто не предназначен для того чтобы вечно жить в бэкраунде, жрать батарейку и выполнять говнокод.


    1. Euzee Автор
      30.10.2017 18:40

      Возможно так и было до прихода JobScheduler спорить не стану, я лично использовал его для Geofencing и по рекомендации доки , в которой и написано «However, in most cases an IntentService is the preferred way to perform simple background operations».