继承和通用AsyncTask问题

时间:2014-12-10 18:06:16

标签: java android generics android-asynctask

我想创建一个在ThreadExceutor上同时执行多个任务(由工厂生成)的类。但是,我遇到了这个类使用的泛型问题。

这涉及三个类/接口:

  1. ConcurrentAsyncTask,一个抽象类,用于管理同步和多次执行任务的结果的合并。它的参数和返回值是通用的。同时执行的所有任务都会扩展它。
  2. TaskFactory是一个通用接口,用于创建要同时执行的任务。这仅用于创建任务的多个实例,以便它们可以并行运行。
  3. TestTaskAsync,一个扩展ConcurrentAsyncTask的示例并行任务。
  4. 我的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)
    

    到目前为止,我找到了两种解决方案

    1. TestTaskAsync的原型更改为public class TestTaskAsync extends ConcurrentAsyncTask<Object, MessageIndex>。使用此方法,我确认传递给Object的{​​{1}}确实是doInBackground的实例。这不是一个真正的首选方法。
    2. IMAPFolder的原型更改为TestTaskAsync并按public class TestTaskAsync<T> extends ConcurrentAsyncTask<T, MessageIndex>创建实例 - 再次无效。 进一步采用第二种方法,我尝试了return new TestTaskAsync<IMAPFolder>(remainingCounter, resultList, callback);,但这导致了与以前相同的问题。
    3. 我使用TestTaskAsync<T extends IMAPFolder>gradle编译我的代码。我错过了什么?

      PS Android Studio告诉我API15行有task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, param); - 我猜这与它有关,但我不太了了解它在这种情况下的含义。

      编辑检查Unchecked generics array creation for vararg parameter的{​​{1}}会提供正确的ActualTypeArgumentsfactory.createTask(remainingCounter, resultList, callback)

1 个答案:

答案 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;
  }
}