AsyncTask我來給你扯會蛋
- (1)要想使用AsyncTask必須繼承實現子類。子類必須至少要重寫一個方法doInBackground,並且絕大多數的情況還要重寫第二個方法onPostExecute,下面是官方的一個簡單的小例子。
-
-
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
-
這是一個下載的小例子,在doInBackgound這個方法中,大家注意看這句話,if (isCancelled()) break;它是寫在了for迴圈裡面的,平時大家下載的時候可能忘記這一點。加上這句話的話,就算我們正在下載中,也是可以直接跳出迴圈的,停止下載任務,這樣更加保險。其他的方法大家都用的很多,這裡就不再說了。
-
開啟一下AsyncTask去下載更是非常簡單:
new DownloadFilesTask().execute(url1, url2, url3);
-
(2)AsyncTask的範型型別。
-
一個非同步任務使用的三種類型如下:
-
1:第一個引數是傳送到doInBackground這個方法裡面的。
-
2:進度型別。這個一般是我們在後臺執行任務時,把進度通過publishProgress這個方法來發不出去,負責更新UI的。
-
3:最後一個引數是結果型別。當我們的doInBackground執行完畢的時候,負責回撥onPostExecute方法,將引數傳遞到這個方法裡面。
-
注意:不是我們所有傳遞的型別都會被非同步任務使用,如果想要傳遞未被使用的型別,我們只需要傳遞Void。
-
private class MyTask extends AsyncTask<Void, Void, Void> { ... }
-
上面這種傳遞Void的寫法,我們平時用的也很多。
-
(3):當一個非同步任務執行時,通過4個步驟來執行。
-
1:onPreExecute,在任務執行前,呼叫UI執行緒,我們可以在這個方法裡面更新UI介面。這一步通常用於設定任務,例如通過在使用者介面顯示一個進度條。
-
2:doInBackground,在onPreExecute這個方法呼叫完畢後,會立即在後臺執行緒中呼叫。這個方法一般用來執行比較耗時的後臺計算。非同步任務的引數被傳遞到這個方法裡面,後臺任務執行完畢後必須返回計算的結果,並將結果傳遞到最後一個步驟中。這一步也可以使用publishprogress(Java Object。[ ])釋出一個或多個單位的進展。這些值被公佈在UI執行緒,在onProgressUpdate(Java Object。[ ])的步驟。
-
3:onProgressUpdate(Java Object。[ ])這個方法會在UI執行緒更新介面顯示在呼叫publishprogress後(Java Object。[ ])。我們一般在doInBackground這個方法中呼叫publishprogress這個方法更新介面,例如我們可以用來更新下載進度條的進度。
-
4:在doInBackground這個方法執行完畢後會回撥onPostExecute這個方法,並且將doInBackgound計算完畢後的結果以引數的形式傳遞到onPostExecute這個方法中。
-
(4):取消一個Task。
-
一個非同步執行緒可以在任何時候通過呼叫cancel方法來取消。呼叫這個方法後,當我們呼叫isCancelled() 這個方法的時候會返回true。當我們呼叫cancel方法後,doInBackground這個方法執行完畢後將不會再呼叫onPostExecute這個方法,將會呼叫onCancelled這個方法。為了確保非同步任務儘快的取消,如果可能的話(一個迴圈中),你應該始終檢查isCancelled()這個方法的返回值。
-
(5)執行緒規則
-
為了讓AsyncTask正常的工作,你應該遵守下面這幾個規則。
-
1:AsyncTask必須在UI執行緒中開啟(?why)。在android4.1.x及以上已經自動在UI執行緒中載入。(此處可能理解有誤。)
-
2:Task的物件必須在UI執行緒中建立。(?why)
-
3:我們啟動執行緒的execute這個方法,必須在UI執行緒中呼叫。
-
4:不要手動呼叫onPreExecute(), onPostExecute(java.lang.Object), doInBackground(java.lang.Object[]), onProgressUpdate(java.lang.Object[]) 這幾個方法。
-
5:非同步任務只能被執行一回,如果我們試圖執行第二回的時候,會丟擲一個異常。
-
(6)記憶體方面
-
1:AsyncTask保證所有的回撥是同步的,除非你顯示同步(這裡理解為你自己去做同步);
-
2:沒法翻譯了。。。說白了,就是你按照正常的步驟走,不會出問題。
-
(7)執行順序
-
1:當第一次介紹的時候,AsyncTask是一個後臺執行的序列執行緒。但是從Build.VERSION_CODES.DONUT,android1.6開始,變成了一個後臺並行執行的執行緒池。從Build.VERSION_CODES.HONEYCOMB,android3.0開始,又變成了任務在一個單一的執行緒中來執行,這是為了避免常見的併發錯誤。如果你真的想要的並行執行,你可以呼叫executeonexecutor與thread_pool_executor。
-
上面介紹完了AsyncTask要我們注意的事項,接下來我們就來分析AsyncTask的原始碼,看看裡面到底是一個什麼東東。AsyncTask的原始碼並不多。我們先看它的成員變數。
-
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); 181 private static final int CORE_POOL_SIZE = CPU_COUNT + 1; 182 private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; 183 private static final int KEEP_ALIVE = 1; 184 185 private static final ThreadFactory sThreadFactory = new ThreadFactory() { 186 private final AtomicInteger mCount = new AtomicInteger(1); 187 188 public Thread More ...newThread(Runnable r) { 189 return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); 190 } 191 }; 192 193 private static final BlockingQueue<Runnable> sPoolWorkQueue = 194 new LinkedBlockingQueue<Runnable>(128); public static final Executor THREAD_POOL_EXECUTOR 200 = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, 201 TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); 208 209 private static final int MESSAGE_POST_RESULT = 0x1; 210 private static final int MESSAGE_POST_PROGRESS = 0x2; 211 212 private static final InternalHandler sHandler = new InternalHandler(); 213 214 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; 215 private final WorkerRunnable<Params, Result> mWorker; 216 private final FutureTask<Result> mFuture; 217 218 private volatile Status mStatus = Status.PENDING; 219 220 private final AtomicBoolean mCancelled = new AtomicBoolean(); 221 private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
我們一個一個分析變數,看一下到底是個什麼東東~ -
(1)cpu_count 這個就不用多說了,是在執行時獲取手機的cpu的數量,例如我的手機雙核,那麼count就是2.
-
(2)CORE_POOL_SIZE,同一時刻能夠執行的執行緒數的數量。當執行緒數量超過這個數目時,其他執行緒就要等待。
-
(3)MAXIMUM_POOL_SIZE,執行緒池的總大小,當我們試圖新增超過這個數量的執行緒時,程式就會崩潰。
-
(4)KEEP_ALIVE,當前活躍的執行緒的數量,這裡我們看到為1,也就是序列執行,這個後面就會在原始碼看到為什麼時序列執行。
-
(5)sThreadFactory,這是一個執行緒工廠。ThreadFactory是一個介面,我們直接new一個介面就相當於寫了一個繼承這個介面的子類。這樣做的好處是我們就不用手動建立執行緒了,也就是不用自己去new Thread了。下面是一個簡單的小例子。
-
package com.test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; class Task implements Runnable{ int taskId; public Task(int taskId) { this.taskId=taskId; } @Override public void run() { System.out.println(Thread.currentThread().getName()+"--taskId: "+taskId); } } class DaemonThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { Thread t=new Thread(r); t.setDaemon(true); return t; } } public class ThreadFactoryTest { public static void main(String[] args) { ExecutorService exec=Executors.newFixedThreadPool(3,new DaemonThreadFactory()); for(int i=0;i<3;i++) { exec.submit(new Task(i)); } exec.shutdown(); } }
這一般是執行緒工廠的用法,DaemonThreadFactory中覆寫的newThread()方法與submit()方法的呼叫關係,也就是說DaemonThreadFactory是如何起作用的。submit()時會呼叫DaemonThreadFactory類的newThread()方法來建立執行緒。 -
這裡相信大家都明白了。
-
(6)sPoolWorkQueue,這個變數是從來儲存Runnable的一個BlockingQueue<Runnable>,關於BlockingQueue,大家可以上網查詢一些資料,這裡大家就先理解為一個執行緒安全的佇列。
-
(7)THREAD_POOL_EXECUTOR,執行緒池,如果要AsyncTask並行執行的話,後面需要用到。
-
(8)MESSAGE_POST_RESULT,一個int型變數,在doInBackground方法執行完畢後,會通過handler將執行結果分發到onPostExecute這個方法裡面,所以我們才可以在這個回撥方法裡面更新UI介面。
-
(9)MESSAGE_POST_PROGRESS,一個int型變數,當我們在doInBackground需要更新進度條顯示的時候,需要通過handler分發訊息,其中訊息的what就是MESSAGE_POST_PROGRESS這個變數。
-
(10)sHandler,繼承系統Handler實現的一個簡單的handler類,用來分發訊息到主執行緒,不然怎麼更新介面。
-
(11)sDefaultExecutor,就是上面我們講解的THREAD_POOL_EXECUTOR這個變數。
-
(12)mWorker,一個WorkerRunnable<Params, Result>變數,下面看一下這個變數的型別的真面目。
-
一個自定義的抽象類,繼承了Callable介面,這個介面中只有一個方法,下面看一下Callable介面的原始碼。private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { 655 Params[] mParams; 656 }
-
這裡應該想到了,就是簡單實現了一個介面,這樣我們在其他的地方就可以呼叫call方法了。public interface Callable<V> { 64 V call() throws Exception; 65}
-
這個方法中還有一個Result型別,我們看看這是個什麼型別。
public abstract class AsyncTask<Params, Progress, Result> {
這是範型,大家可以看到Params,Progress,Result全是定義的範型。 -
(13)mFuture,這是一個FutureTask<Result>型別,我們繼續看這是一個什麼東東~
-
public class FutureTask<V> implements RunnableFuture<V> {
public interface RunnableFuture<V> extends Runnable, Future<V> {
public interface More Future<V> {
就是實現了兩個介面,實現了Future其中的幾個方法,實現了Runnable裡面的run方法,作用就是隻要實現了RunnableFuture這個介面,我們就可以回撥裡面的方法了~很簡單的設計模式,主動呼叫。 -
(14)mStatus,用來記錄AsyncTask的執行狀態,有PENDING, RUNNING,FINISHED,這三個狀態,Pending狀態說明AsyncTask還沒有被執行,等待狀態。Running正在執行狀態。Finished完成狀態。
-
(15)mCancelled,一個AtomicBoolean型別變數,解決執行緒同步問題。當前AsyncTask是否被取消。
-
(16)mTaskInvoked,一個AtomicBoolean型別變數,解決執行緒同步問題。當前AsyncTAsk是否被啟動。
上面介紹完了AsyncTask的成員變數,下面我們就從AsyncTAsk的建構函式開始,一步一步分析AsyncTask是如何工作的,最後我們還要看看原始碼,明白一下為什麼我們使用AsyncTask要遵守Android我們定的準則,我們不遵守可以嗎? -
看看AsyncTask的建構函式:
-
從構造方法中我們可以揣測到,mWorker這個物件一定在我們呼叫execute的時候會回撥call方法,因為mTaskInvoked.set(true);這個標誌為被設定成了true,說明AsyncTask被呼叫執行了。緊接著我們在後面的return語句後面發現了postResult(doInBackground(mParams));這句話,我擦,這不是我們doInBackground方法嗎?把doInBackground方法的結果返回去了,反到哪裡去了?不就是反到了我們平時呼叫的onPostExecute方法裡面的引數了嘛。這個方法裡面我們已經初步看到了一點蛛絲馬跡。那麼postResult這個方法裡面是什麼東東?public AsyncTask() { 282 mWorker = new WorkerRunnable<Params, Result>() { 283 public Result call() throws Exception { 284 mTaskInvoked.set(true); 285 286 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 287 //noinspection unchecked 288 return postResult(doInBackground(mParams)); 289 } 290 }; 291 292 mFuture = new FutureTask<Result>(mWorker) { 293 @Override 294 protected void done() { 295 try { 296 postResultIfNotInvoked(get()); 297 } catch (InterruptedException e) { 298 android.util.Log.w(LOG_TAG, e); 299 } catch (ExecutionException e) { 300 throw new RuntimeException("An error occured while executing doInBackground()", 301 e.getCause()); 302 } catch (CancellationException e) { 303 postResultIfNotInvoked(null); 304 } 305 } 306 }; 307 }
-
private Result postResult(Result result) { 317 @SuppressWarnings("unchecked") 318 Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, 319 new AsyncTaskResult<Result>(this, result)); 320 message.sendToTarget(); 321 return result; 322 }
soga,不他媽就是簡單的handler傳送訊息嘛!訊息的型別是什麼?當然是這個型別的MESSAGE_POST_RESULT,哈哈 -
說到這裡我們就繼續看一眼sHandler這個handler裡面的訊息處理,順序來吧,一回反回去,再講解mFuture這個變數~ps:我喜歡順序講解,一步一步看。。。
-
再看sHandler的原始碼之前,先low一眼AsyncTaskResult這個類的程式碼,發現就是簡單的兩個成員變數~so easyprivate static class AsyncTaskResult<Data> { 660 final AsyncTask mTask; 661 final Data[] mData; 662 663 AsyncTaskResult(AsyncTask task, Data... data) { 664 mTask = task; 665 mData = data; 666 } 667 }
-
好,在sHandler的重寫的handleMessage方法中,我們可以看到最終的呼叫,MESSAGE_POST_RESULT這個訊息下面,呼叫的是result.mTask.finish(result.mData[0]);而mTask就是當前的AsyncTask,接下來我們就看一下finish方法裡面的呼叫。private static class InternalHandler extends Handler { 638 @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) 639 @Override 640 public void handleMessage(Message msg) { 641 AsyncTaskResult result = (AsyncTaskResult) msg.obj; 642 switch (msg.what) { 643 case MESSAGE_POST_RESULT: 644 // There is only one result 645 result.mTask.finish(result.mData[0]); 646 break; 647 case MESSAGE_POST_PROGRESS: 648 result.mTask.onProgressUpdate(result.mData); 649 break; 650 } 651 } 652 }
-
果然不出所料,在finish方法裡面根據isCancelled()方法呼叫onCancelled或者呼叫onPostExecute。豁然開朗啊,同理result.mTask.onProgressUpdate(result.mData);這句程式碼也就沒有什麼大驚小怪的了,這不就是更新進度顯示的回撥方法嘛!草,就是handler傳送訊息啊!private void finish(Result result) { 629 if (isCancelled()) { 630 onCancelled(result); 631 } else { 632 onPostExecute(result); 633 } 634 mStatus = Status.FINISHED; 635 }
-
說了這麼多,接下來我們該回到我們構造函數了。。。下面這個初始化變數我們還沒有講解~
-
這個我們猜測一下,我們構建了一個mWorker物件,裡面有一個call的回撥方法,然後我們mWorker物件通過FutureTask的建構函式傳遞進度,並且重寫了裡面的done方法。那mFuture裡面一定會在重寫的介面方法裡面呼叫mWorker這個介面的call方法~那我們接下來看一下FutureTask的類的原始碼吧。mFuture = new FutureTask<Result>(mWorker) { 293 @Override 294 protected void done() { 295 try { 296 postResultIfNotInvoked(get()); 297 } catch (InterruptedException e) { 298 android.util.Log.w(LOG_TAG, e); 299 } catch (ExecutionException e) { 300 throw new RuntimeException("An error occured while executing doInBackground()", 301 e.getCause()); 302 } catch (CancellationException e) { 303 postResultIfNotInvoked(null); 304 } 305 } 306 };
-
這個類的程式碼稍微多一點,但是我們只需要找到我們關心的即可。我們首先找到建構函式~public class FutureTask<V> implements RunnableFuture<V> { 64 private final Sync sync; 72 73 public FutureTask(Callable<V> callable) { 74 if (callable == null) 75 throw new NullPointerException(); 76 sync = new Sync(callable); 77 } 90 91 public FutureTask(Runnable runnable, V result) { 92 sync = new Sync(Executors.callable(runnable, result)); 93 } 94 95 public boolean isCancelled() { 96 return sync.innerIsCancelled(); 97 } 98 99 public boolean isDone() { 100 return sync.innerIsDone(); 101 } 102 103 public boolean cancel(boolean mayInterruptIfRunning) { 104 return sync.innerCancel(mayInterruptIfRunning); 105 } 109 110 public V get() throws InterruptedException, ExecutionException { 111 return sync.innerGet(); 112 } 116 117 public V get(long timeout, TimeUnit unit) 118 throws InterruptedException, ExecutionException, TimeoutException { 119 return sync.innerGet(unit.toNanos(timeout)); 120 } 130 131 protected void done() { } 139 140 protected void set(V v) { 141 sync.innerSet(v); 142 } 151 152 protected void setException(Throwable t) { 153 sync.innerSetException(t); 154 } 155 156 // The following (duplicated) doc comment can be removed once 157 // 158 // 6270645: Javadoc comments should be inherited from most derived 159 // superinterface or superclass 160 // is fixed. 161 164 165 public void run() { 166 sync.innerRun(); 167 } 176 177 protected boolean runAndReset() { 178 return sync.innerRunAndReset(); 179 } 188 189 private final class Sync extends AbstractQueuedSynchronizer { 190 private static final long serialVersionUID = -7828117401763700385L; State value representing that task is ready to run 192 193 private static final int READY = 0; State value representing that task is running 194 195 private static final int RUNNING = 1; State value representing that task ran 196 197 private static final int RAN = 2; State value representing that task was cancelled 198 199 private static final int CANCELLED = 4; The underlying callable 201 202 private final Callable<V> callable; The result to return from get() 203 204 private V result; The exception to throw from get() 205 206 private Throwable exception; 212 213 private volatile Thread runner; 214 215 Sync(Callable<V> callable) { 216 this.callable = callable; 217 } 218 219 private boolean ranOrCancelled(int state) { 220 return (state & (RAN | CANCELLED)) != 0; 221 } 225 226 protected int tryAcquireShared(int ignore) { 227 return innerIsDone() ? 1 : -1; 228 } 233 234 protected boolean tryReleaseShared(int ignore) { 235 runner = null; 236 return true; 237 } 238 239 boolean innerIsCancelled() { 240 return getState() == CANCELLED; 241 } 242 243 boolean innerIsDone() { 244 return ranOrCancelled(getState()) && runner == null; 245 } 246 247 V innerGet() throws InterruptedException, ExecutionException { 248 acquireSharedInterruptibly(0); 249 if (getState() == CANCELLED) 250 throw new CancellationException(); 251 if (exception != null) 252 throw new ExecutionException(exception); 253 return result; 254 } 255 256 V innerGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException { 257 if (!tryAcquireSharedNanos(0, nanosTimeout)) 258 throw new TimeoutException(); 259 if (getState() == CANCELLED) 260 throw new CancellationException(); 261 if (exception != null) 262 throw new ExecutionException(exception); 263 return result; 264 } 265 266 void innerSet(V v) { 267 for (;;) { 268 int s = getState(); 269 if (s == RAN) 270 return; 271 if (s == CANCELLED) { 272 // aggressively release to set runner to null, 273 // in case we are racing with a cancel request 274 // that will try to interrupt runner 275 releaseShared(0); 276 return; 277 } 278 if (compareAndSetState(s, RAN)) { 279 result = v; 280 releaseShared(0); 281 done(); 282 return; 283 } 284 } 285 } 286 287 void innerSetException(Throwable t) { 288 for (;;) { 289 int s = getState(); 290 if (s == RAN) 291 return; 292 if (s == CANCELLED) { 293 // aggressively release to set runner to null, 294 // in case we are racing with a cancel request 295 // that will try to interrupt runner 296 releaseShared(0); 297 return; 298 } 299 if (compareAndSetState(s, RAN)) { 300 exception = t; 301 releaseShared(0); 302 done(); 303 return; 304 } 305 } 306 } 307 308 boolean innerCancel(boolean mayInterruptIfRunning) { 309 for (;;) { 310 int s = getState(); 311 if (ranOrCancelled(s)) 312 return false; 313 if (compareAndSetState(s, CANCELLED)) 314 break; 315 } 316 if (mayInterruptIfRunning) { 317 Thread r = runner; 318 if (r != null) 319 r.interrupt(); 320 } 321 releaseShared(0); 322 done(); 323 return true; 324 } 325 326 void innerRun() { 327 if (!compareAndSetState(READY, RUNNING)) 328 return; 329 330 runner = Thread.currentThread(); 331 if (getState() == RUNNING) { // recheck after setting thread 332 V result; 333 try { 334 result = callable.call(); 335 } catch (Throwable ex) { 336 setException(ex); 337 return; 338 } 339 set(result); 340 } else { 341 releaseShared(0); // cancel 342 } 343 } 344 345 boolean innerRunAndReset() { 346 if (!compareAndSetState(READY, RUNNING)) 347 return false; 348 try { 349 runner = Thread.currentThread(); 350 if (getState() == RUNNING) 351 callable.call(); // don't set result 352 runner = null; 353 return compareAndSetState(RUNNING, READY); 354 } catch (Throwable ex) { 355 setException(ex); 356 return false; 357 } 358 } 359 } 360}
-
FutureTask的建構函式需要我們傳遞進去一個實現Callable介面的物件,我們前面mWork物件已經實現了這個介面~。然後對callable物件進一步封裝了一下,放到了Sync這個類裡面,原始碼裡面的解釋是這個類用來對FutureTask進行同步的控制,跟進去看一下這個類的建構函式~73 public FutureTask(Callable<V> callable) { 74 if (callable == null) 75 throw new NullPointerException(); 76 sync = new Sync(callable); 77 }
-
Sync(Callable<V> callable) { 216 this.callable = callable; 217 }
建構函式很簡單,就是持有了Callable物件的引用,那麼我們就可以呼叫它的call方法了,哈哈。它裡面的其他方法,我們在後面涉及到的時候再講解~接下來我們分析完了AsyncTask的建構函式,就需要跟進它的啟動過程了。我們通過new 一個AsyncTask,然後呼叫它的execute方法,它就啟動了,這個過程是怎麼樣子的呢? -
這是AsyncTask的啟動方法,繼續跟進~public final AsyncTask<Params, Progress, Result> execute(Params... params) { 535 return executeOnExecutor(sDefaultExecutor, params); 536 }
-
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, 572 Params... params) { 573 if (mStatus != Status.PENDING) { 574 switch (mStatus) { 575 case RUNNING: 576 throw new IllegalStateException("Cannot execute task:" 577 + " the task is already running."); 578 case FINISHED: 579 throw new IllegalStateException("Cannot execute task:" 580 + " the task has already been executed " 581 + "(a task can be executed only once)"); 582 } 583 } 584 585 mStatus = Status.RUNNING; 586 587 onPreExecute(); 588 589 mWorker.mParams = params; 590 exec.execute(mFuture); 591 592 return this; 593 }
看這個方法,需要兩個引數,第一個是一個執行緒池,這個變數我們前面已經創建出來了,它的原始碼如下: -
private static class SerialExecutor implements Executor { 224 final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); 225 Runnable mActive; 226 227 public synchronized void execute(final Runnable r) { 228 mTasks.offer(new Runnable() { 229 public void run() { 230 try { 231 r.run(); 232 } finally { 233 scheduleNext(); 234 } 235 } 236 }); 237 if (mActive == null) { 238 scheduleNext(); 239 } 240 } 241 242 protected synchronized void scheduleNext() { 243 if ((mActive = mTasks.poll()) != null) { 244 THREAD_POOL_EXECUTOR.execute(mActive); 245 } 246 } 247 }
這個類繼承了Exector這個介面,繼續看吧~ -
看來我們重寫了裡面的execute方法,好那我們就看一下SerialExecutor這個類重寫的方法。不難看出,當我們傳遞一個Runnable進去之後,它重新封裝成了一個新的Runnable介面,放到了ArrayDeque這個佇列裡面。下面有一個判斷mActive是否為空,第一次肯定為空,那麼就會呼叫scheduleNext方法,這個方法裡面從ArrayDeque裡面取出一個Runnable,然後放到了THREAD_POOL_EXECUTOR這個執行緒池裡面去執行。在Runnable執行完畢後,無論如何都會在finally裡面呼叫scheduleNext方法,這樣假如我們佇列裡面有很多Runnable的話,就會序列執行下去。public interface Executor { 140 void execute(Runnable command); 141}
-
我們再重新看一下THREAD_POOL_EXECUTOR這個變數~
public static final Executor THREAD_POOL_EXECUTOR 200 = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, 201 TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
第一個引數是同一個時刻能夠執行執行緒的數量;第二個引數是執行緒池的總大小;第三個引數是活躍的執行緒的數量;第四個引數是時間單位;第五個引數是一個執行緒安全的BlockingQueue;第六個引數是一個執行緒工廠,自動產生執行緒。 -
分析完了executeOnExecutor這個方法的引數,我們進一步看裡面的實現~ -
if (mStatus != Status.PENDING) { 574 switch (mStatus) { 575 case RUNNING: 576 throw new IllegalStateException("Cannot execute task:" 577 + " the task is already running."); 578 case FINISHED: 579 throw new IllegalStateException("Cannot execute task:" 580 + " the task has already been executed " 581 + "(a task can be executed only once)"); 582 } 583 } 584 585 mStatus = Status.RUNNING;
首先看上面這部分程式碼,大家一看就明白了當我們一個AsyncTask正在執行狀態的時候,再次呼叫execute方法,會丟擲一個錯誤:不行再次執行一個正在執行的AsyncTask。同理當一個AsyncTask已經執行完畢之後,也不能再次呼叫execute。不是上面的兩種情況的話,就更新當前AsyncTask為正在執行的狀態。 -
onPreExecute(); 588 589 mWorker.mParams = params; 590 exec.execute(mFuture);
此時呼叫的onPreExecute方法意義重大,別人可能就會說了,不就是一個onPreExecute方法的呼叫嗎?這不就是我們平時在下載之前先更新一下介面的onPreExecute方法嘛!對了,就是更新介面的onPreExecute方法。大家現在還沒有忘記AsyncTask的使用準則吧?我們啟動執行緒的execute這個方法,必須在UI執行緒中呼叫。就是這條準則,為什麼AsyncTask必須在UI執行緒中execute,而不能在子執行緒中?這就是原因,因為這個方法android並沒有通過handler傳送出來,而我們平時都在這個方法中更新介面,如果直接在子執行緒中execute,不崩潰才怪! -
下面就是將傳遞進來的引數傳遞給mWorker物件的成員變數,而mWorker是封裝在mFuture物件裡面的,所以最後傳遞的是mFuture物件作為引數。exec執行的程式碼參考如下:再次貼一遍程式碼
-
當然我們的mFuture是實現了Runnable介面的,所以肯定會重寫run方法的,大家可以大膽的猜測一下,mFuture的run方法裡面一定會呼叫mWorker的call方法,不然它怎麼辦~廢話少說,看一下mFuture類中重寫的的run方法~public synchronized void execute(final Runnable r) { 228 mTasks.offer(new Runnable() { 229 public void run() { 230 try { 231 r.run(); 232 } finally { 233 scheduleNext(); 234 } 235 } 236 }); 237 if (mActive == null) { 238 scheduleNext(); 239 } 240 }
-
public void run() { 166 sync.innerRun(); 167 }
繼續跟進~ -
大家在334行看到沒有,果然呼叫了mWorker的call方法,哈哈,說明我們的猜測完全正確~而call方法裡面就是doInBackground的實現了,以後的邏輯就完全聯絡起來了。AsyncTask的整個過程就分析完了。void innerRun() { 327 if (!compareAndSetState(READY, RUNNING)) 328 return; 329 330 runner = Thread.currentThread(); 331 if (getState() == RUNNING) { // recheck after setting thread 332 V result; 333 try { 334 result = callable.call(); 335 } catch (Throwable ex) { 336 setException(ex); 337 return; 338 } 339 set(result); 340 } else { 341 releaseShared(0); // cancel 342 } 343 }
-
分析是分析完了,但是還有幾個小小的疑問。
-
1:AsyncTask為什麼必須在UI執行緒中execute?
-
因為在execute方法中會呼叫onPreExecute方法去更新介面,所以如果不是在UI執行緒中execute,那麼程式就會崩潰。
-
2:AsyncTask為什麼必須在UI執行緒中建立?
-
首先UI執行緒中建立而牽扯出的在AsyncTask中的成員變數,看了一下只有sHandler,而這個Handler的建立肯定會呼叫父類的預設的建構函式,那麼我們就去看一眼handler預設的建構函式,看看裡面是個什麼東東~
-
public More ...Handler(Callback callback, boolean async) { 189 if (FIND_POTENTIAL_LEAKS) { 190 final Class<? extends Handler> klass = getClass(); 191 if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && 192 (klass.getModifiers() & Modifier.STATIC) == 0) { 193 Log.w(TAG, "The following Handler class should be static or leaks might occur: " + 194 klass.getCanonicalName()); 195 } 196 } 197 198 mLooper = Looper.myLooper(); 199 if (mLooper == null) { 200 throw new RuntimeException( 201 "Can't create handler inside thread that has not called Looper.prepare()"); 202 } 203 mQueue = mLooper.mQueue; 204 mCallback = callback; 205 mAsynchronous = async; 206 }
預設的建構函式最後會呼叫Handler的這個建構函式,這裡面需要跟進一下mLooper,看這個變數是怎麼被初始化的。mLooper = Looper.myLooper();我們就繼續跟進~Handler和Looper的關係很重要,而Looper在初始化的時候就會建立一個MessageQueue佇列。我們看一下Looper的myLooper方法。 -
發現有一個sThreadLocal,就是我們當前建立Handler的執行緒。然後我們繼續跟進去~public static Looper myLooper() { 162 return sThreadLocal.get(); 163 }
-
此時的範型T就是我們的Looper物件,我們就不再繼續往裡面跟了。發現最裡面其實就是一個Map,我們可以根據我們的Thread來得到我們的Looper,對於沒有Looper的Thread肯定是得不到Looper的。得不到Looper是不可能成功的建立Handler的,一建立就會報錯~public T get() { 143 Thread t = Thread.currentThread(); 144 ThreadLocalMap map = getMap(t); 145 if (map != null) { 146 ThreadLocalMap.Entry e = map.getEntry(this); 147 if (e != null) 148 return (T)e.value; 149 } 150 return setInitialValue(); 151 }
-
具體的關於Handler的請看我的另一篇部落格:宇哥帶你飛之handler~
-
好了,現在我們就有一個疑問了,我就要在子執行緒中使用AsyncTask,但是我給當前這個執行緒準備好Looper,這樣Handler的建立肯定是沒有問題的。難道是Android官方的表達不夠嚴謹嗎?下面我就寫一個小例子來實驗一下。系統4.0以上-編譯sdk20
-
第一種寫法:
-
private void init() { content = (TextView) findViewById(R.id.content); new Thread() { @Override public void run() { Looper.prepare(); new AsyncTask<Void, Void, Void>() { @Override protected void onPreExecute() { Log.d("xiaoyu","onPreExecute--"+Thread.currentThread().getId()); super.onPreExecute(); } @Override protected Void doInBackground(Void... params) { for (int i = 0; i < 10; i++) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } return null; } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); Log.d("xiaoyu","onPostExecute--"+Thread.currentThread().getId()); Toast.makeText(MainActivity.this, "onPostExecute", Toast.LENGTH_LONG).show(); } }.execute(); Looper.loop(); } }.start(); }
我們先拋開onPreExecute方法不看,根據handler內部建立的機制,handler建立時會自動繫結到當前執行緒,如果當前執行緒沒有Looper會報錯,直接程式崩潰。程式執行沒有崩潰~我現在認為handler得到的是當前執行緒的Looper(現在的理解有錯誤)。 -
繼續實驗:去掉Loop.prepare(),Loop.loop().再次執行程式,應該報錯才對,為什麼沒有報錯,難道我第一次寫法,AsyncTask得到的根本就不是當前執行緒的looper?找問題,最後在ActivityThread中找到答案~
-
public static void main(String[] args) { 5009 SamplingProfilerIntegration.start(); 5010 5011 // CloseGuard defaults to true and can be quite spammy. We 5012 // disable it here, but selectively enable it later (via 5013 // StrictMode) on debug builds, but using DropBox, not logs. 5014 CloseGuard.setEnabled(false); 5015 5016 Environment.initForCurrentUser(); 5017 5018 // Set the reporter for event logging in libcore 5019 EventLogger.setReporter(new EventLoggingReporter()); 5020 5021 Process.setArgV0("<pre-initialized>"); 5022 5023 Looper.prepareMainLooper(); 5024 5025 ActivityThread thread = new ActivityThread(); 5026 thread.attach(false); 5027 5028 if (sMainThreadHandler == null) { 5029 sMainThreadHandler = thread.getHandler(); 5030 } 5031 5032 AsyncTask.init(); 5033 5034 if (false) { 5035 Looper.myLooper().setMessageLogging(new 5036 LogPrinter(Log.DEBUG, "ActivityThread")); 5037 } 5038 5039 Looper.loop(); 5040 5041 throw new RuntimeException("Main thread loop unexpectedly exited"); 5042 }
大家看5032行的程式碼,在程式啟動的時候,呼叫了AsyncTask 的init方法,我們進去看一眼init方法裡面的實現~ -
在主執行緒中建立的AsyTask得到的是主執行緒的Looper,這也就是為什麼我們直接在沒有Looper的子執行緒中建立AsyncTask的物件不會崩潰的原因。但是4.0一下的程式碼不保證AsyncTask一定是得到的是主執行緒的Looper,所以我們還是儘量不要在子執行緒中建立AsyncTask的物件。public static void init() { sHandler.getLooper(); }
-
2:接下來的問題是我們為什麼必須在UI執行緒中呼叫execute方法呢?在子執行緒呼叫不行嗎?從前面的分析中我們得到因為execute方法呼叫的時候會呼叫到onPreExecute方法,而這個方法我們一般都在裡面更新介面,所以應該在子執行緒中呼叫execute方法,會崩潰才對~
-
new Thread() { @Override public void run() { Log.d("xiaoyu","run--"+Thread.currentThread().getId()); new AsyncTask<Void, Void, Void>() { @Override protected void onPreExecute() { Log.d("xiaoyu","onPreExecute--"+Thread.currentThread().getId()); super.onPreExecute(); content.setText("onPreExecute"); } @Override protected Void doInBackground(Void... params) { for (int i = 0; i < 10; i++) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } return null; } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); Log.d("xiaoyu","onPostExecute--"+Thread.currentThread().getId()); Toast.makeText(MainActivity.this, "onPostExecute", Toast.LENGTH_LONG).show(); } }.execute(); } }.start();
上面這種寫法應該崩潰才對啊?為什麼沒有崩潰啊?並且後臺列印執行緒id,onPreExecute方法的呼叫確實不在主執行緒中~ -
08-05 10:48:17.810 2406-2406/? D/xiaoyu﹕ mainThreadId--1 08-05 10:48:17.810 2406-2424/? D/xiaoyu﹕ run--178 08-05 10:48:17.810 2406-2424/? D/xiaoyu﹕ onPreExecute--178 08-05 10:48:17.926 2406-2406/? D/xiaoyu﹕ onPostExecute--1
很奇怪,在子執行緒中竟然更新了介面!!!我了個擦!繼續找問題,難道又是系統給我們做了工作了?但是從列印來看,確實更新介面的時候不是在主執行緒中啊!這個問題還在找中。。。難道是Android給AsyncTask開綠燈了?
-