在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项目,该项目实现了与更大应用程序完全相同的方式,并且遇到了同样的问题。它逐字借用AndroidRuntime
和CustomAsyncTaskComparator
。没有给出视觉反馈。应用程序的进度只是一些LogCat输出。但它提出了这个想法。当两个以上的任务入队时,应用FC。
https://www.dropbox.com/s/lrg4kscgw3f1xwr/ConcurrentTest.tar.gz
答案 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的人,我认为没有太多可以做的。
我目前正在处理类似的问题,但在我的情况下,“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;
}
}