我想创建一个在ThreadExceutor
上同时执行多个任务(由工厂生成)的类。但是,我遇到了这个类使用的泛型问题。
这涉及三个类/接口:
ConcurrentAsyncTask
,一个抽象类,用于管理同步和多次执行任务的结果的合并。它的参数和返回值是通用的。同时执行的所有任务都会扩展它。 TaskFactory
是一个通用接口,用于创建要同时执行的任务。这仅用于创建任务的多个实例,以便它们可以并行运行。 TestTaskAsync
,一个扩展ConcurrentAsyncTask
的示例并行任务。我的ConcurrentAsyncTask
是
public abstract class ConcurrentAsyncTask<Params, Result> extends AsyncTask<Params, Void, List<Result>> {
...
public static <P, R> void executeConcurrent(TaskFactory<P, R> factory, TaskCallback<R> callback, P[] params){
AtomicInteger remainingCounter = new AtomicInteger(params.length);
List<R> resultList = new Vector<R>();
for(P param : params){
ConcurrentAsyncTask<P,R> task = factory.createTask(remainingCounter, resultList, callback);
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, param);
}
}
}
TaskFactory
public interface TaskFactory<Params, Result> {
public ConcurrentAsyncTask<Params, Result> createTask(AtomicInteger remainingCounter, List<Result> resultList, TaskCallback<Result> callback);
}
和扩展类TestTaskAsync
public class TestTaskAsync extends ConcurrentAsyncTask<IMAPFolder, MessageIndex>{
public MessageIndexerAsync(AtomicInteger remainingCounter, List<MessageIndex> messageIndexes, TaskCallback<MessageIndex> callback) {
super(remainingCounter, messageIndexes, callback);
}
@Override
protected List<MessageIndex> doInBackground(IMAPFolder... folders) {
Log.d(TAG, "[doInBackground] Processing "+folders[0].getClass().getCanonicalName());
return new Vector<MessageIndex>();
}
}
我正在尝试执行这样的任务
ConcurrentAsyncTask.<IMAPFolder, MessageIndex>executeConcurrent(
new TaskFactory<IMAPFolder, MessageIndex>() {
@Override
public ConcurrentAsyncTask<IMAPFolder, MessageIndex> createTask(AtomicInteger remainingCounter, List<MessageIndex> resultList, TaskCallback<MessageIndex> callback) {
return new TestTaskAsync(remainingCounter, resultList, callback);
}
},
new TaskCallback<MessageIndex>(){
@Override
public void exec(List<MessageIndex> messageIndexes) {
}
},
imapFolders.toArray(new IMAPFolder[imapFolders.size()])
);
此后我得到一个未被捕获的例外。添加UncaughtExceptionHandler
后,我发现该问题属于AsyncTask
类型,正在接收Params
12-10 17:58:33.272 10774-10818/(...) E/ConcurrentAsyncTask﹕
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:299)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
at java.util.concurrent.FutureTask.run(FutureTask.java:239)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:856)
Caused by: java.lang.ClassCastException: java.lang.Object[] cannot be cast to com.sun.mail.imap.IMAPFolder[]
at (...).TestTaskAsync.doInBackground(TestTaskAsync.java:19)
at android.os.AsyncTask$2.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:856)
到目前为止,我找到了两种解决方案
TestTaskAsync
的原型更改为public class TestTaskAsync extends ConcurrentAsyncTask<Object, MessageIndex>
。使用此方法,我确认传递给Object
的{{1}}确实是doInBackground
的实例。这不是一个真正的首选方法。 IMAPFolder
的原型更改为TestTaskAsync
并按public class TestTaskAsync<T> extends ConcurrentAsyncTask<T, MessageIndex>
创建实例 - 再次无效。
进一步采用第二种方法,我尝试了return new TestTaskAsync<IMAPFolder>(remainingCounter, resultList, callback);
,但这导致了与以前相同的问题。 我使用TestTaskAsync<T extends IMAPFolder>
为gradle
编译我的代码。我错过了什么?
PS Android Studio告诉我API15
行有task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, param);
- 我猜这与它有关,但我不太了了解它在这种情况下的含义。
编辑检查Unchecked generics array creation for vararg parameter
的{{1}}会提供正确的ActualTypeArguments
和factory.createTask(remainingCounter, resultList, callback)
答案 0 :(得分:0)
在创建数组params:Object []的那一刻,你的应用程序对你将要传递的类型一无所知,因为你的泛型类型P []与你编写Object []相同。因此,当您调用executeOnExecutor时,您的方法将传递Object []数组,而不仅仅是参考Object []。如您所知,您无法将Object []强制转换为ChildOfObject []。您应该使用IMAPFolder类型的参数覆盖您的方法executeOnExecutor ...然后它应该工作。
但是如果你编写task.execute(),你也会有同样的异常,因为你的execute方法也有Object []类型的参数,而不是你的自定义泛型。用子类型的params覆盖方法是不好的做法。对你来说,最好写下一个:
@Override
protected List<MessageIndex> doInBackground(Object... folders) {
if (folder[0] instanceof IMAPFolder) {
Log.d(TAG, "[doInBackground] Processing "+((IMAPFolder)folders[0]).getClass().getCanonicalName());
return new Vector<MessageIndex>();
} else {
return null;
}
}