Android開發藝術探索 - 第11章 Android的線程和線程池

Android的主線程主要是運行四大組件以及處理其與用戶的交互;而子線程用於執行耗時任務,如網絡請求,I/O操作。

1.Android中的線程形態

  1. AsyncTask
    在線程池中執行後臺任務,其封裝了Thread和Handler,通過execute方法執行AsyncTask。關鍵方法:
    • onPreExecute:在執行execute方法的線程中運行,一般爲主線程,用於執行在開始異步任務之前的一些準備工作。
    • doInBackground:在線程池中運行,用於執行異步任務。方法中通過publishProgress方法更新任務進度,進而調用到onProgressUpdate方法。此方法需要返回計算結果給onPostExecute。
    • onProgressUpdate:主線程中運行,異步任務更新進度時被回調。
    • onPostExecute:主線程中運行,異步任務執行完畢被回調。
    • onCancelled:主線程中運行,當異步任務被取消時被回調,此時onPostExecute不會被調用。

限制:
* AsyncTask的類必須要在主線程中加載。所以第一次訪問AsyncTask時需要在主線程中進行,Android 4.1以上會自動完成這個過程,在ActivityThread的main方法中。
* AsyncTask的對象必須在主線程中創建。
* execute方法必須在UI線程調用。
* 不要直接調用AsyncTask的關鍵方法。
* 一個AsyncTask對象只能執行一次,即execute只能調用一次,否則會拋出異常。
* 可以通過exectueOnExecutor來指定線程池執行任務。

原理:
從execute方法開始。execute會使用默認的線程池,即SerialExecutor。一個進程中所有的AsyncTask都會在這個線程池中排隊(static成員):

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

之後調用executeOnExecutor,先執行onPreExecute方法,然後線程池開始執行:

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    ...
    mStatus = Status.RUNNING;

    onPreExecute();

    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}

SerialExecutor的實現。在實例化AsyncTask的時候,會創建一個FutureTask對象,該類是一個併發類,充當了Runnable的作用,其中會包含execute傳入的參數。當線程池開始執行,首先將當前的FutureTask對象插入到任務隊列中,如果當前沒有正在活動的任務,則調用scheduleNext方法執行下一個任務;同時一個任務執行完畢,也會調用scheduleNext方法執行下一個任務:

private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        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) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

scheduleNext方法則通過另一個線程池,THREAD_POOL_EXECUTOR去具體執行任務,該線程池是個併發線程池。默認情況下,AsyncTask會通過SERIAL_EXECUTOR接收Runnable,串行的每次執行一個,而具體的執行任務交由THREAD_POOL_EXECUTOR;可以讓AsyncTask併發的去執行Runnable,只要在其execute方法中,直接指定THREAD_POOL_EXECUTOR這個線程池就可以了。
當任務開始執行,會調用FutureTask的run方法。在實例化AsyncTask的時候,會創建一個WorkerRunnable,同時綁定到FutureTask上,FutureTask的run方法會調用WorkerRunnable的call方法,此時doInBackground方法便會被執行:

    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) {
            ...
        };
    }

doInBackground執行完畢,會調用postResult發送出結果,其任務就是通過AsyncTask內部的Handler發送一個msg:

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

該Handler在創建時,Looper指定爲main looper,保證其回調發生在主線程。當任務處理完成,Handler在主線程中執行AsyncTask#finish方法,當更新進度則回調AsyncTask#onProgressUpdate方法:

    private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            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;
            }
        }
    }

finish方法中,則根據是否AsyncTask是否被取消了,去回調onCancel或者onPostExecute:

private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}
  1. HandlerThread
    繼承自Thread,並在run方法中創建消息隊列和開啓了消息循環,之後便可直接通過該Thread創建相應的Handler去執行任務。其run方法:
@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}
  1. IntentService
    繼承自Service,內部使用了HandlerThread和Handler來實現,任務在子線程中的串行執行。onCreate方法中創建HandlerThread的實例,並通過其Looper創建Handler:
@Override
public void onCreate() {
    // TODO: It would be nice to have an option to hold a partial wakelock
    // during processing, and to have a static startService(Context, Intent)
    // method that would launch the service & hand off a wakelock.

    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();

    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
}

而Handler的具體實現,是回調onHandleIntent方法,將具體的任務實現交由其子類,任務執行完畢後調用stopSelf嘗試停止service。所有任務完成後,service銷燬:

private final class ServiceHandler extends Handler {
    public ServiceHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
        onHandleIntent((Intent)msg.obj);
        stopSelf(msg.arg1);
    }
}

client使用時,直接startService,IntentService#onStart方法中就會通過Handler將Intent包裹爲msg發出,等待Looper去執行:

@Override
public void onStart(@Nullable Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}

2.Android中的線程池

優點:

  • 重用線程池中的線程,減少創建和銷燬帶來的性能開銷;
  • 能控制最大併發數,避免大量線程搶佔資源導致阻塞;
  • 能對線程進行簡單的管理,提供定時執行和間隔循環執行等功能。

Android中的線程池爲接口Executor,具體實現類爲ThreadPoolExecutor,通過爲其配置不同的參數,可以創建不同的線程池。

  1. ThreadPoolExecutor
    常用的構造方法:
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory) {
* corePoolSize:核心線程數。默認情況下,核心線程會在線程池中一直存活,即使處於閒置狀態。
* maximumPoolSize:最大線程數。包括了核心線程和非核心線程。
* keepAliveTime:非核心線程閒置的超時時間,超過這個時間就就會被回收。當把allowCoreThreadTimeOut設置爲true,同時也會作用於核心線程的。
* unit:keepAliveTime的時間單位。
* workQueue:線程池中的任務隊列,通過execute方法提交的Runnable會添加到這裏。
* threadFactory:創建線程時使用的工具類。
* RejectedExecutionHandler類型的不常用參數,用於指定,當無法執行新任務時,線程池會回調該handler的rejectedExecution方法,默認實現是直接拋出一個RejectedExecutionHaException異常。

ThreadPoolExecutor執行任務的規則:
1. 如果線程池中的核心線程數未達到上限,則直接啓動一個核心線程去執行;
2. 如果核心線程數已達上限,則將任務插入到隊列中排隊;
3. 如果隊列也滿了,則嘗試創建非核心線程去執行;
4. 如果總的線程數已經達到了上限,此時已經無法執行任務,此時便會調用RejectedExecutionHandler#rejectedExecution方法,通知調用者。

ThreadPoolExecutor在AsyncTask中的創建示例:
```
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);

    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};

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

/**
 * An {@link Executor} that can be used to execute tasks in parallel.
 */
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;
}
```
* 核心線程數爲2~4個
* 最大線程數爲2*Cpu核心數+1
* 核心線程有閒置超時
* 任務隊列容量爲128
  1. 四類線程池
    四類線程池都是通過Executors的靜態方法進行創建。
    • FixedThreadPool:具有固定的核心線程,且不會被回收,當所有的核心線程處於活躍狀態,新任務則排隊等待。能夠更快的響應外界的需求。
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    
    • CachedThreadPool:沒有核心線程,具有無限數量的非核心進程,且線程閒置超時會被回收;其任務隊列相當於空集,所以不會緩存任何任務,有新任務便會馬上執行。適合執行大量且耗時少的任務。同時因爲閒置時線程會被回收,所以其閒置狀態幾乎不佔用系統資源。
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    
    • ScheduledThreadPool:具有固定數量的核心線程和無限的非核心線程,非核心線程閒置超時會被回收。用於執行定時任務和週期性任務。
        public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
            return new ScheduledThreadPoolExecutor(corePoolSize);
        }
    
        // ScheduledThreadPoolExecutor.java
    
        private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
    
        public ScheduledThreadPoolExecutor(int corePoolSize) {
            super(corePoolSize, Integer.MAX_VALUE,
                  DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
                  new DelayedWorkQueue());
        }
    
    • SingleThreadPool:只有一個核心線程,所有的任務按順序執行。
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章