Android :: AsyncTask DoInBackground Does Not Run

Posted in :

遇到一個離奇的bug,就是某一個 activity 裡的 AsyncTask 只能執行一次,離開 Activity,再重新 new AsyncTask 會變成只有 onPreExecute()  被執行,doInBackground() 完成不會被執行到!

screenshot_2016-10-01-07-53-13-426_com-android-settings
測試環境是:Android 6.0.1 + MIUI 8.0.2.0

這真的是很離奇的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);
    }
}

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *