遇到一個離奇的bug,就是某一個 activity 裡的 AsyncTask 只能執行一次,離開 Activity,再重新 new AsyncTask 會變成只有 onPreExecute() 被執行,doInBackground() 完成不會被執行到!
這真的是很離奇的Bug! google +debug 了4小時,試了很多解法都無解!同一支手機之前都沒遇過。
在相同的source code 之下,使用Android模擬器在 Android 4.3 環境中,怎麼debug 都不會有問題。
初步「猜測」可能是小米手機升級(System Updates)之後造成的,workaround 的解法是使用ConcurrentAsyncTask()來執行原本的 AsyncTask.execute() 即可。
資料來源: https://github.com/haiwen/seadroid
import android.os.AsyncTask; import android.os.Build; import android.support.annotation.NonNull; import android.util.Log; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RunnableFuture; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * Make sure an AsyncTask is executed in parallel across different version of * Android * @see asynctask executing tasks serially or concurrently {@link http://www.jayway.com/2012/11/28/is-androids-asynctask-executing-tasks-serially-or-concurrently/} */ public class ConcurrentAsyncTask { private static final String DEBUG_TAG = "ConcurrentAsyncTask"; /** * We extend ThreadPoolExecutor to log exceptions */ private static class SeadroidThreadPoolExecutor extends ThreadPoolExecutor { /* Copied over from AsyncTask */ private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE = CPU_COUNT + 1; private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final int KEEP_ALIVE = 1; private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue(128); private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) { return new Thread(r, "SeadroidAsyncTask #" + mCount.getAndIncrement()); } }; public SeadroidThreadPoolExecutor() { super(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); } @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); // some exceptions are stored inside the Future, extract them // See ThreadPoolExecutor.afterExecute() javadoc for an explanation if (t == null && r instanceof Future<?>) { try { Object result = ((Future<?>) r).get(); } catch (CancellationException ce) { t = ce; } catch (ExecutionException ee) { t = ee.getCause(); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); // ignore/reset } } if (t != null) Log.e(DEBUG_TAG, "Uncaught exception in thread pool", t); } } private static final ThreadPoolExecutor threadPoolExecutor = new SeadroidThreadPoolExecutor(); public static <T> void execute(AsyncTask<T, ?, ?> task, T...args) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR1) { task.execute(args); } else { task.executeOnExecutor(threadPoolExecutor, args); } } @NonNull public static Future<?> submit(Runnable runnable) { return threadPoolExecutor.submit(runnable); } @NonNull public static <T> Future<T> submit(Callable<T> task) { return threadPoolExecutor.submit(task); } }