- AsyncTask我们平时用的再熟悉不过了,让我们说的话,它是一个什么东东?我个人的理解是它是一个Thread+handler来实现的一个简单的更新UI的这个一个东西,不过这中说话应该不会让人信服。接下来咱们就分析它的源代码。
-
- 1:首先看一下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开绿灯了? -
-
-
-
-
-
-
-
-