Android的主線程主要是運行四大組件以及處理其與用戶的交互;而子線程用於執行耗時任務,如網絡請求,I/O操作。
1.Android中的線程形態
- 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;
}
- 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;
}
- 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,通過爲其配置不同的參數,可以創建不同的線程池。
- 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
- 四類線程池
四類線程池都是通過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>())); }