Android中的职位经理

时间:2015-01-15 14:14:16

标签: android android-service job-scheduling wakelock robospice

我有一项任务是在Android应用中运行多个不同的工作。每个作业都是长期运行的,并且很多地使用网络,数据库和文件系统。每个作业都可以由用户手动运行或由AlarmManager调度。每个作业运行到最后都非常重要,因此需要在用户离开应用程序后继续运行,或者甚至在用户根本不打开应用程序时。乔布斯有一些ID属性:

class Job {
    int id;
}

我需要这个假设的JobManager来接收作业并按ID排序。如果id = 1的作业已在运行,则JobManager应跳过id = 1的所有后续作业,直到此作业完成。但是如果提交的作业的id = 2,则它被接受并且可以与第一份作业并行运行。

作业也应该保持唤醒锁定直到完成,就像在CommonsWare的WakefulIntentService中完成一样。

我有几个想法如何实现这一点,但都有它们的缺点:

  1. Service类的子类,它始终在后台运行,并在出于某种原因被杀死时自动重新启动。缺点:它消耗资源即使没有运行任何东西,它也在UI线程上运行,所以我们必须管理一些可以像往常一样被系统杀死的线程,每个客户端都必须启动服务而没有人知道何时停止它。
  2. 来自CommonsWare的WakefulIntentService。缺点:因为它是IntentService,它只是按顺序运行,因此无法检查现有的正在运行的作业。
  3. 布尔"运行"每个作业的数据库中的标志。每次我们想要工作时检查它。缺点:对数据库的请求太多,难以正确实现,有时2个相同的工作仍然可以并行运行,不确定标记是否停留" true"如有任何意外错误。
  4. 现有的图书馆已为此目的而设。至于现在除了CWAC-Wakeful我发现:

    但我仍然不知道如何使用这些库来运行一个集中服务,它可以接受来自任何其他Activity,Service,BroadcastReceiver,AlarmManager等的作业,按ID排序并并行运行

  5. 请告诉我在这种情况下可以使用什么解决方案。

    更新:请参阅下面我自己的解决方案。我不确定,它是否适用于所有可能的情况。如果您发现可能出现的任何问题,请发表评论。

3 个答案:

答案 0 :(得分:2)

这似乎适用于Lollipop上的新JobScheduler API,然后你必须围绕它来实现包含sdk实现缺失的所有功能。

如果您需要在Lollipop以下的版本上实现此功能,那么有一个compat库。

答案 1 :(得分:1)

如果有人遇到同样的问题,这就是我提出的解决方案。我使用了Robospice lib,因为它是在服务上运行某些作业并将结果同步回Activity的最强大的方法。由于我没有找到任何方法将此lib与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) {
            ...
        }
    });

答案 2 :(得分:-1)

Workmanager将帮助您按任意顺序安排任务。您可以轻松地为要添加的作业设置约束,同时还可以通过JobScheduler API或报警管理器设置许多其他优势。看一下这个视频的简短介绍 - https://www.youtube.com/watch?v=pErTyQpA390(WorkManager at 21:44)。

  

编辑:更新了我的ans以显示新API的功能   你不需要id来处理这个工作。您可以简单地将任务排入队列,其余的将由API本身处理。

一些工作案例场景

  • WorkManager.getInstance() .beginWith(workA) // Note: WorkManager.beginWith() returns a // WorkContinuation object; the following calls are // to WorkContinuation methods .then(workB) .then(workC) .enqueue();

  • WorkManager.getInstance() // First, run all the A tasks (in parallel): .beginWith(workA1, workA2, workA3) // ...when all A tasks are finished, run the single B task: .then(workB) // ...then run the C tasks (in any order): .then(workC1, workC2) .enqueue();