我覺得AsyncTask算是Android源碼裏面比較傑出的一個了.裏面涉及的知識點很多,並且運用起來也很合理.
在安卓裏,使用後臺線程,並且需要與主線程交互的方式,最直觀的就是new Thread+Handler 和 AsyncTask..
在new Thread+Hander後,安卓開拓大神又創建了AsyncTask.我覺得有兩個理由.
第一.不用關注創建線程,使用線程,管理線程的邏輯,只需要關注業務的3個方法.任務開始前準備,任務執行,任務完成後的工作
第二.AsyncTask使用Java語言的線程池,或者更廣義來說,使用了java.util.concurrent這個並行編程大禮包.使得多線程操作更高效更得心應手.
AsyncTask關於執行的3個方法.
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params);}
public static void execute(Runnable runnable) { sDefaultExecutor.execute(runnable); }
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) {....}
從名字就可以看出,設計到線程池Executor知識.所以不可避免,需要對Executor這個家族瞭解瞭解.
關於Executor
Executor和Executors的關係,類似於Collection和Collections的關係.一個是定義規範的接口,一個工具類.
Executors經典的方法有3個
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
可以看到這3者都用到了ThreadPoolExecutor這個子類.(其中使用了newSingleThreadExecutor方法使用了子類FinalizableDelegatedExecutorService對ThreadPoolExecutor進行了一層封裝).這個ThreadPoolExecutor構造方法接受了幾個變量.
第一個corePoolSize表示核心線程數,第二個maximumPoolSize表示最大線程數.第三個和第四個,一個表示數量一個表示單位,聯合起來表示這個線程多久沒使用會被銷燬.最後一個表示任務排隊的機制,需要要好好講一下.
這裏先講一下ThreadPoolExecutor運行和調度的具體流程.
if (當前線程數量 < corePoolSize)
新建線程,並處理請求.
else if (當前線程數量 >= corePoolSize && workQueue未裝滿)
放到我們的workQueue中等待處理,當我們的線程處理完任務,就會從這個queue裏面取出新任務來處理.這個wordQueue也就是我們的構造函數第五個參數.
else if (corePoolSize <= 當前線程數量 < maxumumPoolSize && workQueue裝滿)
新建線程來處理任務.
else
使用RejectedExecutionHandler來做拒絕處理
還有一點就是當線程數大於corePoolSize && 線程等待時間比keepAliveTime長,就會銷燬線程.
回到Executors的三個方法中.來講一下這3個方法產生出來的線程池有什麼特點.
- newSingleThreadExecutor表示只有一個線程的線程池
corePoolSize和maximumPoolSize都是1,表示永遠只有1個線程.第三個參數傳0表示永不銷燬.通過一個LinkedBlockingQueue來表示無限長度的隊列.可以塞到內存溢出爲止的任務.
- newFixedThreadPool表示依靠傳參來產生固定數量的線程池
corePoolSize和maximumPoolSize都是用傳過來的參數固定線程池數量.比如傳了數字10就表示固定只有10個線程.當然起碼要來10個任務才能激活創建這10個線程.其他的同上.
- newCachedThreadPool表示無界線,同時有多少任務需要執行就產生多少個線程的線程池.
SynchronousQueue其實也屬於BlockingQueue的子類.跟LinkedBlockingQueue有兄弟關係.它的特點就是沒有容量,也就是沒有隊列.
詳細一點來說,首先它有兩個方法put和offer來放元素,有兩個方法take/poll來取元素.對於每次放元素操作,都需要等到一次取元素來同時進行.反過來也是,每次取元素操作,都需要等到一次放元素操作同時進行.如果這頭生產者放元素,那頭消費者並沒有取元素,就會一直阻塞.所以根據ThreadPoolExecutor使用流程,沒有隊列相當於塞滿隊列.
所以newCachedThreadPool使用這個SynchronousQueue來實現每次有新任務來,要麼用池子裏的空閒線程,要麼新建線程去處理,而不是排隊等待着被執行.
關於Callable
一句話總結的話,Callable就是帶返回值,還可以拋出異常的Runnable.
不帶返回值,不能拋出異常的Runnable能幹的東西太少了.
所以Java1.5帶來了Callable接口以及使用Callable需要的FutureTask等(其實更準確來說應該帶來了java.util.concurrent包)
關於Callable的用法有兩種,
public class CallableAndFuture {
public static void main(String[] args) {
Callable<Integer> callable = new Callable<Integer>() {
public Integer call() throws Exception {
return new Random().nextInt(100);
}
};
FutureTask<Integer> future = new FutureTask<Integer>(callable);
new Thread(future).start();
try {
Thread.sleep(5000);// 可能做一些事情
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
1.這種是寫個新建一個FutureTask持有一個Callable,然後新建一個Thread來執行.
可以看到由於Thread只能執行Runnable接口的任務,所以是使用Runnable的子類FutureTask,也不是Thread直接去執行Callable接口.
同時FutureTask也實現了Future,主要代表這個任務有返回值,可以取消,可以觀測到任務是否完成.
public class Test {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
MyCallable task = new MyCallable();
Future<Integer> result = executor.submit(task);
executor.shutdown();
try {
Thread.sleep(1000);
System.out.println("task運行結果" + result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
static class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("子線程在進行計算");
Thread.sleep(3000);
int sum = 0;
for (int i = 0; i < 100; i++)
sum += i;
return sum;
}
}
}
2.這種是使用線程池,給線程池提交的一個任務.
可以看到ExecutorService裏面有3個方法
- <T> Future<T> submit(Callable<T> task);
- <T> Future<T> submit(Runnable task, T result);
- Future<?> submit(Runnable task);
Futrure.get()是阻塞方法,如有必要,可以調用get(long timeout,TimeUnit unit)表示最多等待多少時間,時間過完還等不到結果就拋出異常.
更常用的用法是這樣
while (!futureTask.isDone()) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
V = futureTask.get();
另外Future接口還提供了cancel方法和判斷是否已經取消的isCancelled方法.
關於AsyncTask
回到AsyncTask,感覺可以分爲這幾點
1.AsyncTask中的線程池策略
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 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
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
核心線程數是CPU+1個. 最大線程數CPU*2+1. 保持時間1秒. 這個是基於android-23的源碼來定的.比如android-24又會把corePoolSize定成最多4個.
使用了BlockingQueue且最大隊列數量爲128.
使用了自定義的ThreadFactory,目的是將來給新建的線程起名爲"AsyncTask #"方便標識.AtomicInteger確保多線程操作下數據能準確,線程安全.
但是神奇的是,AsyncTask用的並不是這個線程池.而是用的自定義的SerialExecutor
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);
}
}
}
這個類裏面有一個mTasks隊列,用的是ArrayDeque.看了下源碼知道,這個類其實意思是雙向循環隊列.裏面實現的思路是用了數組,一個int型頭(指針)位置,一個int型尾(指針)位置.初始容量是8,當超過這個大小會使用當前大小X2加大容量.
重寫Executor的executor方法.
裏面會對Runnable裏面的邏輯進行封裝一層,即執行完任務或者報異常都會接着執行scheduleNext方法.
scheduleNext會從隊列裏拿出新任務繼續執行,直到隊列爲空.
然後把這個封裝後的Runnable丟到隊列裏.接着判斷當前有沒有任務在進行,沒有的話開始第一個任務.
其實意思就是實現順序執行任務的意思.所以這個SerialExecutor名字也很符合,Serial表示串行,串行線程池.
回到"神奇的地方",神奇就是在創建了一個多線程池THREAD_POOL_EXECUTOR,卻用來串行執行任務.
比如我丟進去1000個任務,每個處理需要1秒.添加打印出當前線程信息.
static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(1000L);
Log.e(TAG, "Thread:" + Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
02-02 02:38:22.324 1741-1754/com.yao.mytestproject E/HomeActivity: Thread:Thread[AsyncTask #1,5,main]
02-02 02:38:23.366 1741-1773/com.yao.mytestproject E/HomeActivity: Thread:Thread[AsyncTask #2,5,main]
02-02 02:38:24.408 1741-1791/com.yao.mytestproject E/HomeActivity: Thread:Thread[AsyncTask #3,5,main]
02-02 02:38:25.448 1741-1808/com.yao.mytestproject E/HomeActivity: Thread:Thread[AsyncTask #4,5,main]
02-02 02:38:26.463 1741-1825/com.yao.mytestproject E/HomeActivity: Thread:Thread[AsyncTask #5,5,main]
02-02 02:38:27.504 1741-1825/com.yao.mytestproject E/HomeActivity: Thread:Thread[AsyncTask #5,5,main]
02-02 02:38:28.544 1741-1825/com.yao.mytestproject E/HomeActivity: Thread:Thread[AsyncTask #5,5,main]
出來的log發現剛開始5個任務會讓線程池創建5個新線程執行(因爲coolPoolSize根據cpu數量來定,我這裏是4+1=5),接着就是一直用第5號線程執行任務.空着的4個線程,有那麼點浪費的意思啊.
2.上面的Runnable任務怎麼來的,畢竟我們只是重寫了doInBackgroud方法——關於AsyncTask的構造函數
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(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);
}
}
};
}
...
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
AsyncTask的構造函數會新建一個WorkerRunnable.它是繼承Callable類型,同時持有一個泛型數組,將來用來存放傳過來的可變參數的.
重寫裏面的call方法,裏面會執行doInBackgroud,得到的result會發送個某個handler.
這只是把執行的流程包裝成一個Callable,並不是立即執行裏面的流程.之後用新建FutureTask持有這個Callable.
前面講過,只有用new FutureTask(Callable)的方式,才能把Callable當成Runnable使用,最終才能交給THREAD_POOL_EXECUTOR去執行execute(Runnable)代碼.
3.關於運行在UI線程的3個函數.onPostExecute.onPreExecute.onProgressUpdate
private static InternalHandler sHandler;
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@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;
}
}
}
前面講執行完doInBackgroud,會把執行結果發送到一個Handler.就是這個靜態的InternalHandler.
getHandler典型的單例獲取方法.InternalHandler用的是Looper.getMainLooper,代表運行在UI線程.
裏面有兩個case,一個是執行處理結果,一個是反饋進度的POST_PROGRESS.
反饋進度時候我們只需要在代碼裏調用publishProgress方法,就能發送消息到這個Handler裏面,最終執行onProgressUpdate
4.最終,回到AsyncTask的執行的開始,execute方法
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
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;
}
AsyncTask的execute方法執行,並且傳了params參數.會使用默認的串行線程池執行executeOnExecutor方法.
裏面首先判斷這個AsyncTask是否在運行或已經運行完畢.(這也告訴我們,new出來AsyncTask只能運行一次)
然後會執行onPreExecute(此時還在UI線程).然後給我們的任務賦值參數後正式開始執行.
關於"神奇的地方"的猜想
首先傳說安卓1.5是AsyncTask是順序執行的.1.6~2.3.2是多個線程並行執行.3.0後又變成順序執行的.所以可能由於歷史遺留那擁有多個線程的THREAD_POOL_EXECUTOR就保留下來了.
然後畢竟這個線程池的聲明是 public static final Executor THREAD_POOL_EXECUTOR,重點是公開的.
所以我們可以這樣用AsyncTask
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) 就會使用當前CPU數量的最佳策略來並行得跑任務.
還不滿足.可以使用executeOnExecutor(Executors.newFixedThreadPool(10))這樣的傳參決定容量的池子來跑.
再不滿足可以使用executeOnExecutor(Executors.newCachedThreadPool())不限制並行數量的池子來跑.
再再不滿足,請自定義池子跑吧.