Executor上的AsyncTask和PriorityBlockingQueue实现问题

时间:2014-06-27 22:16:48

标签: android multithreading android-asynctask priority-queue

this SO question的浪潮中以及来自another one的许多提示,我正在尝试使用可以优先处理的任务来实现AsyncTask变体。

在我的CustomAsyncTask课程中,我有:

public abstract class CustomAsyncTask<Params, Progress, Result> {

    private static int CORE_POOL_SIZE = 1;
    private static int MAXIMUM_POOL_SIZE = 1;

    private static final int KEEP_ALIVE = 1;

    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "CustomAsyncTask #" + mCount.getAndIncrement());
        }
    };

    private static final BlockingQueue<DownloadTask> pPoolWorkQueue =
            new PriorityBlockingQueue<DownloadTask>(10, new DownloadTasksComparator());

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static Executor PRIORITY_THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, (PriorityBlockingQueue) pPoolWorkQueue, sThreadFactory);

   //...
}

比较器:

public class DownloadTasksComparator implements Comparator<DownloadTask> {

    @Override
    public int compare(DownloadTask arg0, DownloadTask arg1) {
        int res;

        if (arg0 == null && arg1 == null) {
            res = 0;
        } else if (arg0 == null) {
            res = -1;
        } else if (arg1 == null) {
            res = 1;
        }

        res = arg0.getPriority() - arg1.getPriority();

        return res;
    }
}

在扩展DownloadTask的{​​{1}}班级中,我有CustomAsyncTask整数字段和priority方法。

我将任务执行称为:

getPriority()

这样做:如果池大小为1,则逐个执行下载;如果池大小为2,等等。

注意:优先级整数具有任意值:

DownloadTask dt = new DownloadTask(..., PRIORITY_NORMAL, ...);
dt.executeOnExecutor(CustomAsyncTask.PRIORITY_THREAD_POOL_EXECUTOR);

但如果我将任务称为:

public static final int PRIORITY_HIGH = 10;
public static final int PRIORITY_NORMAL = 1;

我有DownloadTask dt = new DownloadTask(..., PRIORITY_HIGH, ...); dt.executeOnExecutor(CustomAsyncTask.PRIORITY_THREAD_POOL_EXECUTOR);

然后

java.lang.ClassCastException: my.pkg.name.CustomAsyncTask$3 cannot be cast to my.pkg.name.DownloadTask

全部来自at my.pkg.name.DownloadTasksComparator.compare(DownloadTasksComparator.java:1) at java.util.concurrent.PriorityBlockingQueue.siftUpUsingComparator(PriorityBlockingQueue.java:334) at java.util.concurrent.PriorityBlockingQueue.offer(PriorityBlockingQueue.java:447) at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1295) at my.pkg.name.CustomAsyncTask.executeOnExecutor(CustomAsyncTask.java:494) at my.pkg.name.GetDownloadTaskListener$1.finishDownload(GetDownloadTaskListener.java:180) at my.pkg.name.DownloadTask.onPostExecute(DownloadTask.java:330) at my.pkg.name.DownloadTask.onPostExecute(DownloadTask.java:1) at my.pkg.name.CustomAsyncTask.finish(CustomAsyncTask.java:536) at my.pkg.name.CustomAsyncTask.access$0(CustomAsyncTask.java:532) at my.pkg.name.CustomAsyncTask$InternalHandler.handleMessage(CustomAsyncTask.java:549) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:4745) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) at dalvik.system.NativeStart.main(Native Method)

我真的没有线索......

编辑:此时,我已经包装了一个小型Eclipse项目,该项目实现了与更大应用程序完全相同的方式,并且遇到了同样的问题。它逐字借用AndroidRuntimeCustomAsyncTaskComparator。没有给出视觉反馈。应用程序的进度只是一些LogCat输出。但它提出了这个想法。当两个以上的任务入队时,应用FC。

https://www.dropbox.com/s/lrg4kscgw3f1xwr/ConcurrentTest.tar.gz

3 个答案:

答案 0 :(得分:2)

CustomAsyncTask一样,{p> AsyncTask使用static队列。目前,该队列中的 Everything 将通过您的DownloadTasksComparator运行。但是,DownloadTasksComparator仅适用于DownloadTask。只要你只使用DownloadTask,而不是CustomAsyncTask的其他子类,你就可以了。但是,显然你有一些其他的匿名内部类扩展CustomAsyncTask,那不是DownloadTask

CustomAsyncTask设为abstract,使用getPriority()方法作为abstract方法。将DownloadTasksComparator重命名为CustomAsyncTaskComparator并将其与CustomAsyncTask个实例进行比较。然后,您的CustomAsyncTask的其他子类需要实现自己的getPriority()方法,以使它们与工作队列中的DownloadTask实例一起排序。

答案 1 :(得分:1)

正如您在查看AsyncTask实现时可能已经注意到的那样,它在内部使用FutureTask来处理后台任务,这就是传递给Executor的可能性排队等待它的工作队列。由于您已经在推导自己的实现,因此可以将FutureTask替换为包含对AsyncTask的引用的自定义衍生产品,以便从Comparator实施中进行访问。

此外,您应该在子类中使用Executor方法,而不是替换自定义AsyncTask派生词的默认静态executeOnExecutor(),以便以通用方式使用它

答案 2 :(得分:0)

问题是您用来构造ThreadPoolExecutor的PriorityBlockingQueue<CustomAsyncTask>。 ThreadPoolExecutor应该接受BlockingQueue<Runnable>。 ThreadPoolExecutor使用此队列对runnables进行排队(通过调用execute(Runnable r)提交runnables)。

在AsyncTask的情况下,它使用FutureTask对象调用Executor.execute(Runnable r),因此这个FutureTask对象由执行者排队,而不是AsyncTask本身。因此,当比较器尝试将runnable转换为DownloadTask时,抛出异常。我猜my.pkg.name.CustomAsyncTask$3可能是一个内部可运行的类。

以下是AsyncTask源代码的一部分:

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task is already running.");
            case FINISHED:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task has already been executed "
                        + "(a task can be executed only once)");
        }
    }

    mStatus = Status.RUNNING;

    onPreExecute();

    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}

如果您尝试使用此PriorityBlockingQueue方法优先运行AsyncTask,则应该将传递给execute方法的runnable对象子类化,使其具有优先级。您正在使用自定义asynctask,因此您可以完全控制变量。

对于那些试图优先运行原始AsyncTask的人,我认为没有太多可以做的。 mFuture,你的双手被束缚。

我目前正在处理类似的问题,但在我的情况下,“priority”是时间戳,我可以在runnable实现中设置它,所以它有点不受AsyncTask的限制。

这是我的代码,以防万一它可以帮助某人。抱歉我的英语不好!

/* Executor used by AsyncTask. Use it with executeOnExecutor()*/
class PriorityExecutor extends ThreadPoolExecutor{

    //workQueue is a instance of PriorityBlockingQueue<Runnable>
    public PriorityExecutor(int corePoolSize, int maximumPoolSize,
            long keepAliveTime, TimeUnit unit,
            BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,threadFactory);

    }

    //wrap the runnable passed in.
    @Override
    public void execute(Runnable command) {
        super.execute(new PriorityRunnableWrapper(command));
    }
}

//a wrapper class
class PriorityRunnableWrapper implements Runnable ,Comparable<PriorityRunnableWrapper>{

    long addedTime;//the "priority"
    Runnable r;

    public PriorityRunnableWrapper(Runnable r){
        this.r = r;
        addedTime = System.nanoTime();//in my case the timestamp is the priority
    }

    @Override
    public void run() {
        r.run();
    }

    @Override
    public int compareTo(PriorityRunnableWrapper another) {
        if(addedTime == another.addedTime)return 0;
        return addedTime - another.addedTime > 0 ? -1 : 1;
    }

}