每日一道面試題(第10期)---談談對HandlerThread與AsyncTask的理解

零零碎碎的東西老是記不長久,僅僅學習別人的文章也只是他人咀嚼後留下的殘渣。無心中發現了這個每日一道面試題 ,想了想若是隻是簡單地去思考,那麼不只會收效甚微,甚至難一點的題目本身可能都懶得去想,堅持不下來。因此不如把每一次的思考、理解以及別人的看法記錄下來。不只加深本身的理解,更要激勵本身堅持下去。java

前言

前幾天的那個面試弄得我一臉懵逼,如今有點空閒時間,仔細的探討一下當時的問題。印象最深的就是關於 HandlerThread 與 AsyncTask 的問題。首先列一下探討過程當中想到的問題以及面試時問的問題。android

  • HandlerThread 與 AsyncTask 的大體實現原理
  • HandlerThread 與 AsyncTask 完成耗時任務後會怎麼樣
  • HandlerThread 與 AsyncTask 中的耗時事件處理是異步仍是同步,可不能夠變爲另外一種處理
  • HandlerThread 與 AsyncTask 內存泄漏的可能性
  • HandlerThread 與 AsyncTask 使用中的注意事項
  • HandlerThread 與 AsyncTask 的實際應用場景
  • 給定大量的耗時任務,耗時操做並不連續,耗時時長長短不一,用哪一個

源碼解析

先簡單講講這兩個的源碼吧,都挺簡單的,並無那麼複雜。如下源碼來自於 Android-28git

HandlerThread

HandlerThread 其實是 Thread+Looper+Handler 的一個簡單封裝。github

HandlerThread 類繼承 Thread 類,因此須要 start() 方法來開啓線程。面試

HandlerThread handlerThread = new HandlerThread("workThread");
    handlerThread.start();
    Handler threadHandler = new Handler(handlerThread.getLooper()){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //根據不一樣的 message 類型處理不一樣的耗時任務
        }
    };
複製代碼

這是簡單的使用方法。建立 Handler 時須要傳入 HandlerThread 中的 Looper 對象,這樣 handlerMessage 方法就會在子線程中處理耗時任務。須要耗時任務能夠經過 threadHandler.sendMessage() 發送消息,而後下 handlerMessage 方法中進行處理。網絡

源碼中最重要的就是一個重寫的 run 方法。併發

@Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();//喚醒線程能夠獲取 Looper 對象了
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
複製代碼

也很簡單,在咱們執行 handlerThread.start() 開啓一個線程後,就會執行此方法。經過 Looper.prepare() 在此線程中建立一個 Looper 對象,而後通知其餘線程能夠獲取 Looper 對象,設置線程優先級。onLooperPrepared() 是一個空的方法,咱們能夠重寫此方法進行一些 Looper 開啓 loop 循環以前的準備。less

一切都準備好以後,就是經過 Handler 發送消息,而後在 handlerMessage() 中進行耗時操做。簡單說明一下,Handler 類中的 handlerMessage() 方法在哪一個線程中執行,是由 Handler 中的 Looper 對象所在的線程決定的,這是由於在 loop 循環中經過 msg.target.dispatchMessage()--->handleMessage() 間接地調用了 handlerMessage 方法,而 Looper.loop 是在子線程中執行的。具體可看 android 的消息機制詳解---每日一道面試題(第 9 期)---談談 Handler 機制和原理 異步

AsyncTask

AsyncTask 則是線程池與 Handler 的封裝ide

一些基本使用就不在詳細說明了,主要來看看源碼。首先是構造方法,有三個。無參、Handler、Looper,前兩個都會調用第三個構造方法。

public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }
複製代碼

有點長,但其實就三步操做。

  • 初始化 mHandler。mHandler 是用來轉換線程的。 當傳入的 Looper 對象不爲空且不是主線程的 Looper 時,就建立一個新的 Handler,不然就獲取主線程的 handler。getMainHandler() 就是一個簡單的建立新的 Handler 對象並將主線程的 Looper 對象傳入進去的操做。
  • 初始化 mWorker。mWorker 是一個實現了 Callable 接口的類的對象。初始化時重寫了 call 方法,耗時任務 doINbackGround 被封裝在這裏面執行。
  • 初始化 mFuture。mFuture 是 FutureTask 對象,是 Runnable 與 Future 的子類。將 mWorker 傳入 mFuture 對象中。後面就是將此對象傳入線程池中進行調度。

一般使用時經過 execute 方法開啓任務,看看源碼中幹了什麼。

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
    
    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;
    }
複製代碼

execute 中執行的是 executeOnExecutor 方法,並傳入 sDefaultExecutor 與耗時任務須要的參數。首先是檢查狀態,mStatus 是一個枚舉變量,有 PENDING、RUNNING、FINSHED 三種狀態,這三種狀態都是惟一的,按 PENDING---RUNNING---FINISHED 順序,初始化對象時是 PENDING,在 executeOnExecutor 中變爲 RUNNING,在 finish 方法中更新爲 FINSHED 狀態。所以能夠看出 executor 方法只能在一個對象中執行一次,屢次執行就會拋出異常。而後更新狀態,調用 onPreExecute 方法,咱們能夠種重寫此方法作些進行耗時操做前的準備。傳入參數,而後就是 exec.execute 提交任務,也就是構造函數中包裝好的 FutureTask 對象。

這個 exec 是成員變量 sDefaultExecutor,是 AsyncTask 內部定義的靜態類,實現了 Executor 接口。

private static class SerialExecutor implements Executor {
        //雙端隊列,按照先進先出的原則儲存 FutureTask 對象
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            //對傳入的 mFuture 又進行了一次封裝,以便於串行處理任務
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        //執行完上一個耗時任務後,選取下一個任務
                        scheduleNext();
                    }
                }
            });
            //選取任務
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                //由此可看出 SerialExecutor 只負責任務的串行處理,真正的耗時任務操做是交給 THREAD_POOL_EXECUTOR 線程池進行調度
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
複製代碼

這是一個靜態類,也就是說全部的耗時任務都要通過此類進行串行處理。SerialExecutor 就是爲了使耗時任務可以串行的被處理才存在的,真正處理耗時任務的則是 THREAD_POOL_EXECUTOR 線程池。

//得到沒有睡眠的 CPU 數量
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    // We want at least 2 threads and at most 4 threads in the core pool,
    // preferring to have 1 less than the CPU count to avoid saturating
    // the CPU with background work
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE_SECONDS = 30;

    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);//cas 操做的 int 變量
        public Thread newThread(Runnable r) {
            //記錄建立線程的數量
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };

    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);
    
    public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        //設置核心線程池也受設置的存活時間的影響
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }
複製代碼

以上就是建立了一個新的線程池,並賦值給靜態變量 THREAD_POOL_EXECUTOR,這部分操做是在靜態代碼塊中進行的,也就是說只會在被類加載的時候執行一次。全部的耗時任務都是在這僅有的一個線程池中執行任務。簡單說下這個線程池中的參數。

  • CORE_POOL_SIZE:核心線程池數量,這個定義的有點複雜,官方解釋說,老是但願核心線程數量在 2-4 之間,而且更但願比正在工做的 CPU 數量少 1.
  • MAXIMUM_POOL_SIZE:所存在的最大線程數量,也就是核心線程與非核心線程之和的數量。默認爲正在工做的 CPU 數量的 2 倍+1。
  • KEEP_ALIVE_SECONDS:非核心線程完成任務後保持存活的時間,超時將被銷燬。若是設置了 allowCoreThreadTimeOut(true) 屬性,則核心線程也受此約束。默認爲 30
  • TimeUnit.SECONDS:上一個屬性的單位,這裏是秒。
  • sPoolWorkQueue:儲存耗時任務的隊列,這裏用的 LinkBlockingQueue,基於鏈表實現的阻塞隊列,並將儲存數量控制在了 128 個。
  • sThreadFactory:線程工廠,爲線程池提供新線程的建立。ThreadFactory 是一個接口,裏面只有一個 newThread 方法。 默認爲 DefaultThreadFactory 類。

剩下的就是線程池中調度 mFuture 執行耗時任務,執行其中的 mFuture 中 Callable 接口的 call 方法。其實就是上面說到的構造方法中初始化的 mWorker,其中對 call 方法進行了重寫,在來了解下。

mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                //標記耗時任務已被執行
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    //若發生異常則設置任務爲取消狀態
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    //不管如何,處理結果
                    postResult(result);
                }
                return result;
            }
        };
複製代碼

能夠看出,在進行耗時操做後,不管是處理完,還有發生異常,都要 postResult() 方法進行收尾。

private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }
複製代碼

用 Handler 包裝了一個信息,發送了出去。標記爲 MESSAGE_POST_RESULT,意思就是耗時任務的結果。這個 Handler,就是構造函數中初始化的那個 Handler 對象,只不過在經過 getMainHandler 中是用自定義的靜態內部 Handler 類進行了包裝。

private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }
    
    private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            //AsyncTask 的靜態內部類,方便傳遞結果數據與對應的 AsyncTask 對象
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }
複製代碼

發送的消息就在這裏被處理了。若是是 MESSAGE_POST_RESULT,就調用 finish 方法;若是是 MESSAGE_POST_PROGRESS,就是 onProgressUpdate(result.mData) 方法,也就是咱們能夠用來重寫更新進度的操做。MESSAGE_POST_PROGRESS 類消息只有在你調用 publishProgress 方法時纔會被調用。

private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }
複製代碼

finish 方法中會根據 mCancel 的狀態決定調用 onCancelled(result) 仍是 onPostExecute(result),也就是說這兩個只會調用其中一個方法,這兩個方法也是咱們在使用 AsyncTask 須要重寫的方法。mCanael 咱們能夠經過調用 cancel() 方法改變狀態。

public final boolean cancel(boolean mayInterruptIfRunning) {
        mCancelled.set(true);
        //在 mFuture 中中斷線程
        return mFuture.cancel(mayInterruptIfRunning);
    }
複製代碼

整個內部大體的流程就差不過了。將耗時任務封裝進 FutureTask,SerialExecutor 對 FutureTask 在進行包裝使耗時任務能夠串行執行,最後由 THREAD_POOL_EXECUTOR 線程池進行真正的耗時任務調度處理。

關於HandlerThread與AsyncTask的FAQ

大體實現原理

  • HandlerThread:開啓一個子線程,建立新的 Looper 並開啓 looper 循環,使子線程一直存在,直到 loop 循環退出。 使用時初始化 HandlerThread,並 new 一個 Handler 並傳入 Handler 中的 Looper,就能夠經過 handler 發送消息,在 handlerMessage 中接受消息並執行相應的耗時任務。
  • AsyncTask:經過 executor 將封裝了 doInBackground 中的耗時任務的 FutureTask 對象傳入到 SerialExecutor 中,SerialExecutor 串行的將任務發送給 THREAD_POOL_EXECUTOR 線程池進行調度。

完成耗時任務後會怎麼樣

  • HandlerThread 是 loop 循環+Handler 消息處理機制,也就是說,只要 loop 循環不退出,那麼線程就不會中止,須要處理耗時任務只須要 Handler 發送對應類型的消息便可。
  • AsyncTask 的耗時任務是交給線程池去調度,耗時任務完成後線程的存活與否有線程池的特性決定。而 AsyncTask 的 execute 方法執行一次後,就不能夠在此調用。由於內部狀態是嚴格按照 PENDING、RUNNING、FINISHED 順序變化,不可逆轉。在 executeOnExecutor 方法中會檢查狀態並拋出異常。

耗時事件處理是異步仍是同步,可不能夠變爲另外一種處理

HandlerThread

HandlerThread 是在子線程的 loop 循環中進行的耗時操做,只有當前的耗時操做完成,才能獲取下一個消息處理,因此是串行的。至於變爲並行的,不能夠。簡單驗證下

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",Locale.US);
    Handler threadHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        HandlerThread handlerThread = new HandlerThread("workThread");
        handlerThread.start();
        threadHandler = new Handler(handlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.e("HandlerThread", "message" + msg.what + ":" + df.format(new Date()));
            }
        };
    }
    
    public void onLoginClick(View v){
        int i = 0;
        threadHandler.sendEmptyMessage(++i);
        threadHandler.sendEmptyMessage(++i);
        threadHandler.sendEmptyMessage(++i);
        threadHandler.sendEmptyMessage(++i);
        threadHandler.sendEmptyMessage(++i);
    }
複製代碼

AsyncTask

自定義一個簡單的 AsyncTask

static class MyAsyncTask extends AsyncTask<Void, Void, String>{
        String name = "AsyncTask";

        private MyAsyncTask(String name){
            super();
            this.name = name;
        }
        
        @Override
        protected String doInBackground(Void... voids) {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return name;
        }

        @Override
        protected void onPostExecute(String string) {
            super.onPostExecute(string);
            Log.e("AsyncTask", string + ":" + df.format(new Date()));
        }
    }
複製代碼

當咱們進行一連串的耗時任務時,就會串行處理

new MyAsyncTask("AsyncTask#1").execute();
    new MyAsyncTask("AsyncTask#2").execute();
    new MyAsyncTask("AsyncTask#3").execute();
    new MyAsyncTask("AsyncTask#4").execute();
    new MyAsyncTask("AsyncTask#5").execute();
    new MyAsyncTask("AsyncTask#6").execute();
    new MyAsyncTask("AsyncTask#7").execute();
    new MyAsyncTask("AsyncTask#8").execute();
複製代碼

從結果中能夠看出嚴格的按照先入先出串行處理任務。那麼能不能變爲併發的呢?在源碼分析中咱們知道串行是受 SerialExecutor 對象進行控制的,而此對象是在 execute 中經過 return executeOnExecutor(sDefaultExecutor, params) 傳入進去的。而恰好咱們能夠調用 executeOnExecutor 方法。因此咱們能夠自定義線程池甚至傳入 AsyncTask 的 THREAD_POOL_EXECUTOR 線程池跳過 SerialExecutor 的串行控制,直接用線程池進行併發處理任務。

new MyAsyncTask("AsyncTask#1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    new MyAsyncTask("AsyncTask#2").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    new MyAsyncTask("AsyncTask#3").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    new MyAsyncTask("AsyncTask#4").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    new MyAsyncTask("AsyncTask#5").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    new MyAsyncTask("AsyncTask#6").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    new MyAsyncTask("AsyncTask#7").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    new MyAsyncTask("AsyncTask#8").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
複製代碼

從結果中能夠看出,是四個併發進行的任務處理。爲何是 4 個呢?這個是線程池的調度問題。測試用的手機正在工做 CPU 數目是 8,因此核心線程池數量是 Math.max(2, Math.min(CPU_COUNT - 1, 4)) = 4,最大的線程數量是 CPU_COUNT * 2 + 1 = 17。線程池首先會用核心線程處理任務,核心線程滿了後,後面的認爲就會被放入阻塞隊列,當隊列也滿後,再來任務的話,就會建立非核心線程,取隊首任務進行處理,後來的任務放入隊尾。若是非核心線程也滿了,在來任務就會拋出異常。有個圖可能看着會更明白些。
因此說任務少是就只是核心線程在併發處理任務。

那麼爲何要默認串行處理任務,那樣豈不是丟失了線程池的最大優點?

這是由於默認線程池其實所能容下的線程並很少,就拿 8 核的例子來看,最大線程數爲 17,阻塞隊列容量爲 128,加起來所能容下的最大線程數爲 17+128=145,在高併發的狀況下很容易就會滿,而且 THREAD_POOL_EXECUTOR 對象在整個應用程序中是惟一的。因此默認是串行處理,若是真的有高併發處理的狀況,能夠根據需求自定義線程池進行併發處理。

內存泄漏的可能性

  • HandlerThread 的內存泄漏在於必定要記得在 Activity 銷燬時手動退出線程。由於 loop 循環是一個死循環,若是不手動退出,就會一直存在。
  • AsyncTask 的內存泄漏在於必定要用靜態內部類的形式,內部類會默認持有 Activity 的引用,而 Activity 與 AsyncTask 的生命週期並不能肯定 Activity 更長。

使用中的注意事項

  • 關於 HandlerThread,我沒怎麼用過,查了網上資料,只有一個說法。給 HandlerThread 設置不一樣的優先級,cpu 會根據不一樣的線程優先級對全部線程進行優化。
  • 之前的資料都是說 AsyncTask 的對象要在主線程建立,可能後來源碼作了修改,如今這個在我看來並不須要,由於內部轉換線程的 Handler 的建立並不依賴當前線程的 Looper 對象,而是經過 Looper.getMainLooper() 獲取的主線程 Looper。固然,onPreExecute 方法是會受 AsyncTask 對象建立時的線程影響的,由於此方法並非經過 Handler 的消息傳遞而執行的。
  • 還有一個就是 AsyncTask 取消線程執行的問題,cancel 並不能真正的馬上終止程序的執行,它只是改變了標記狀態的變量,固然 cancel 方法中的 mFuture.cancel(mayInterruptIfRunning),若是傳入 true 也會在 FutureTask 層進行終止線程的操做,但對於一些不可中止的操做,也只能等待任務完成而後根據標記變量狀態調用 onCancelled 仍是 onPostExecute。因此說咱們應該在 doInBackground 方法中儘量的不斷進行狀態的檢驗,在須要返回時儘早的退出線程。

應用場景

  • HandlerThread 實際上就是 Thread+Looper 的結合,因此也就適用於單線程+多個耗時任務的場景,好比網絡請求、文件讀寫。而對於耗時長的多個任務,我我的認爲由於串行執行的關係,HandlerThread 並不適用。
  • AsyncTask 主要用來耗時任務完成後與 UI 線程的交互,不過默認是串行的,可能這也是官方說明 AsyncTask 儘量執行耗時幾秒的操做,不過能夠直接經過 executeOnExecutor 變爲並行處理任務。

給定大量的耗時任務,耗時操做並不連續,耗時時長長短不一,用哪一個

對於這個面試時拋出的問題,我簡單說下個人理解。由於我實際開發經驗少的可憐,因此說的可能有錯誤或者很片面,包括上面的幾個問題的回答。如今網上的資料真的不敢隨便相信,有的還自相矛盾,我突然明白麪試時面試官問我日常都看誰的文章,知道哪些在 Android 方面比較專業的人士的用意了。

大量的耗時操做,若是任務之間沒有什麼關聯的話,在我看來若是是串行處理的話都不怎麼好,由於會阻塞後面的任務,而任務之間並不須要有個先後執行的順序。因此在 AsyncTask 中並行處理比較好。而若是任務之間有關聯,則需串行執行,此時就要看這些耗時任務的執行邏輯是否一致,若是不一致的話那就要自定義多個 AsyncTask,也非常麻煩。在我看來 AsyncTask 更多的是強調與 UI 線程的交互吧。

其實對於 Android 中的耗時任務處理,HandlerThread、IntentService、AsyncTask、ThreadPoolExecutor 這幾個具體應用場景,有哪些差異,還真的說不出來個因此然來,還需努力。(總之就是菜(滑稽))

相關文章
相關標籤/搜索