0

У меня есть задача запустить несколько разных заданий в приложении для Android. Каждое задание длительное время, и многие из них используют сеть, базу данных и файловую систему. Каждое задание может запускаться вручную пользователем или планироваться с помощью AlarmManager. Очень важно, чтобы каждое задание выполнялось до конца, поэтому его нужно продолжать работать после того, как пользователь покидает приложение или даже когда пользователь вообще не открывает приложение. Работа есть некоторые ID атрибута, как это:Менеджер вакансий в Android

class Job { 
    int id; 
} 

мне нужен этот гипотетический JobManager получить работу и сортировать их по ID. Если работа с id = 1 уже запущена, JobManager должен пропустить все последующие задания с id = 1, пока это задание не будет завершено. Но если задание отправляется с id = 2, оно принимается и может выполняться параллельно с первым заданием.

Работы также должны содержать блокировку бодрствования до завершения, как это делается в режиме WakefulIntentService CommonsWare.

У меня есть несколько идей, как это реализовать, но все они имеют свои недостатки:

  1. Подкласс класса обслуживания, который работает всегда в фоновом режиме и автоматически перезапускается, когда убили по какой-то причине. Недостатки: он потребляет ресурсы, даже если ничего не работает, он работает в потоке пользовательского интерфейса, поэтому нам нужно управлять некоторыми потоками, которые могут быть убиты системой как обычно, каждый клиент должен запустить службу, и никто не знает, когда ее остановить.
  2. WakefulIntentService от CommonsWare. Недостатки: поскольку это IntentService, он выполняется только последовательно, поэтому он не может проверить существующее текущее задание.
  3. Булево значение «работает» в базе данных для каждой работы. Проверяйте это каждый раз, когда мы хотим запустить задание. Недостатки: слишком много запросов к db, их сложно реализовать должным образом, иногда два равных задания все равно могут выполняться параллельно, не уверен, что флаги остаются «истинными» в случае какой-либо неожиданной ошибки.
  4. Существующие библиотеки, предназначенные для этой цели. На данный момент, кроме CWAC-бодрствующего я нашел:

    , но до сих пор я не знаю, как использовать эти библиотеки для запуска точно один централизованный сервис, который будет принимать задания из любой другой Activity, Service, BroadcastReceiver, AlarmManager и т. д., сортировать их по идентификатору и запускать параллельно.

Пожалуйста, сообщите мне, какое решение может быть использовано в этом случае.

ОБНОВЛЕНИЕ: См. Ниже мое собственное решение. Я не уверен, если он работает во всех возможных случаях. Если вам известны какие-либо проблемы, которые могут возникнуть с этим, прокомментируйте.

ответ

2

Это похоже на новый API JobScheduler на Lollipop, тогда вам придется сделать оболочку вокруг него, чтобы реализовать все функции, которые не реализованы в реализации sdk.

Существует библиотека compat, если вам необходимо реализовать это на версиях ниже Lollipop.

1

Если кто-то сталкивается с той же проблемой, вот решение, с которым я столкнулся.Я использовал Robospice lib, потому что это самый надежный способ запуска некоторых заданий на службе и синхронизация результатов с Activity. Поскольку я не нашел способов использовать эту библиотеку с WakeLocks, я расширил 2 класса: SpiceManager и SpiceRequest. Новые классы, WakefulSpiceManager и WakefulSpiceRequest, фактически заимствуют идеи CommonsWare о WakeLocks, реализация очень похожа.

WakefulSpiceManager:

public class WakefulSpiceManager extends SpiceManager { 
    private static final String NAME = "WakefulSpiceManager"; 
    private static volatile PowerManager.WakeLock wakeLock; 
    private Context context; 

    public WakefulSpiceManager(Context context, Class<? extends SpiceService> spiceServiceClass) { 
     super(spiceServiceClass); 
     this.context = context; 
     start(context); 
    } 

    private static synchronized PowerManager.WakeLock getLock(Context context) { 
     if (wakeLock == null) { 
      PowerManager mgr = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 

      wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, NAME); 
      wakeLock.setReferenceCounted(true); 
     } 

     return wakeLock; 
    } 

    public <T> void execute(WakefulSpiceRequest<T> request, RequestListener<T> requestListener) { 
     PowerManager.WakeLock lock = getLock(context); 
     lock.acquire(); 
     request.setLock(lock); 

     // explicitly avoid caching 
     super.execute(new CachedSpiceRequest<T>(request, null, ALWAYS_EXPIRED), requestListener); 
    } 
} 

WakefulSpiceRequest:

public abstract class WakefulSpiceRequest<R> extends SpiceRequest<R> { 
    private PowerManager.WakeLock lock; 

    public WakefulSpiceRequest(Class<R> clazz) { 
     super(clazz); 
    } 

    public void setLock(PowerManager.WakeLock lock) { 
     this.lock = lock; 
    } 

    @Override 
    public final R loadDataFromNetwork() throws Exception { 
     try { 
      return execute(); 
     } finally { 
      if (lock.isHeld()) { 
       lock.release(); 
      } 
     } 
    } 

    public abstract R execute() throws Exception; 
} 

Так в основном здесь мы приобретаем замок каждый раз, когда мы собираемся, чтобы послать запрос от WakefulSpiceManager. После этого блокировка передается в WakefulSpiceRequest. Когда запрос завершает работу, он очищает блокировку с помощью метода release() - это произойдет, даже если активность с WakefulSpiceManager уже уничтожена.

Теперь мы используем эти классы в порядке обычной Robospice в, с той лишь разницей, что нам нужно пройти только WakefulSpiceRequests выполнить на WakefulSpiceManager:

WakefulSpiceManager manager = new WakefulSpiceManager(context, MyService.class); 
    manager.execute(new WakefulSpiceRequest<MyResult>(MyResult.class) { 
     @Override 
     public MyResult execute() throws Exception { 
      return ... 
     } 
    }, new RequestListener<MyResult>() { 
     @Override 
     public void onRequestFailure(SpiceException e) { 
      ... 
     } 

     @Override 
     public void onRequestSuccess(MyResult result) { 
      ... 
     } 
    }); 
Смежные вопросы