AsyncTask我来给你扯会蛋

  • 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>变量,下面看一下这个变量的类型的真面目。

        • private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
          655        Params[] mParams;
          656    }
          一个自定义的抽象类,继承了Callable接口,这个接口中只有一个方法,下面看一下Callable接口的源代码。

        • public interface Callable<V> {
          64    V call() throws Exception;
          65}
          这里应该想到了,就是简单实现了一个接口,这样我们在其他的地方就可以调用call方法了。

        • 这个方法中还有一个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的构造函数:

        • 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    }
          从构造方法中我们可以揣测到,mWorker这个对象一定在我们调用execute的时候会回调call方法,因为mTaskInvoked.set(true);这个标志为被设置成了true,说明AsyncTask被调用执行了。紧接着我们在后面的return语句后面发现了postResult(doInBackground(mParams));这句话,我擦,这不是我们doInBackground方法吗?把doInBackground方法的结果返回去了,反到哪里去了?不就是反到了我们平时调用的onPostExecute方法里面的参数了嘛。这个方法里面我们已经初步看到了一点蛛丝马迹。那么postResult这个方法里面是什么东东?

        • 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:我喜欢顺序讲解,一步一步看。。。

        • private 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的源代码之前,先low一眼AsyncTaskResult这个类的代码,发现就是简单的两个成员变量~so easy

        • 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    }
          好,在sHandler的重写的handleMessage方法中,我们可以看到最终的调用,MESSAGE_POST_RESULT这个消息下面,调用的是result.mTask.finish(result.mData[0]);而mTask就是当前的AsyncTask,接下来我们就看一下finish方法里面的调用。

        • private void finish(Result result) {
          629        if (isCancelled()) {
          630            onCancelled(result);
          631        } else {
          632            onPostExecute(result);
          633        }
          634        mStatus = Status.FINISHED;
          635    }
          果然不出所料,在finish方法里面根据isCancelled()方法调用onCancelled或者调用onPostExecute。豁然开朗啊,同理result.mTask.onProgressUpdate(result.mData);这句代码也就没有什么大惊小怪的了,这不就是更新进度显示的回调方法嘛!草,就是handler发送消息啊!

        • 说了这么多,接下来我们该回到我们构造函数了。。。下面这个初始化变量我们还没有讲解~

        •  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        };
          这个我们猜测一下,我们构建了一个mWorker对象,里面有一个call的回调方法,然后我们mWorker对象通过FutureTask的构造函数传递进度,并且重写了里面的done方法。那mFuture里面一定会在重写的接口方法里面调用mWorker这个接口的call方法~那我们接下来看一下FutureTask的类的源代码吧。

        • 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}
          
          这个类的代码稍微多一点,但是我们只需要找到我们关心的即可。我们首先找到构造函数~

        • 73     public FutureTask(Callable<V> callable) {
          74         if (callable == null)
          75             throw new NullPointerException();
          76         sync = new Sync(callable);
          77     }
          FutureTask的构造函数需要我们传递进去一个实现Callable接口的对象,我们前面mWork对象已经实现了这个接口~。然后对callable对象进一步封装了一下,放到了Sync这个类里面,源代码里面的解释是这个类用来对FutureTask进行同步的控制,跟进去看一下这个类的构造函数~

        • Sync(Callable<V> callable) {
          216            this.callable = callable;
          217        }

          构造函数很简单,就是持有了Callable对象的引用,那么我们就可以调用它的call方法了,哈哈。它里面的其他方法,我们在后面涉及到的时候再讲解~接下来我们分析完了AsyncTask的构造函数,就需要跟进它的启动过程了。我们通过new 一个AsyncTask,然后调用它的execute方法,它就启动了,这个过程是怎么样子的呢?

        •  public final AsyncTask<Params, Progress, Result> execute(Params... params) {
          535        return executeOnExecutor(sDefaultExecutor, params);
          536    }
          这是AsyncTask的启动方法,继续跟进~

        • 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这个接口,继续看吧~

        • public interface Executor {
          140    void execute(Runnable command);
          141}
          看来我们重写了里面的execute方法,好那我们就看一下SerialExecutor这个类重写的方法。不难看出,当我们传递一个Runnable进去之后,它重新封装成了一个新的Runnable接口,放到了ArrayDeque这个队列里面。下面有一个判断mActive是否为空,第一次肯定为空,那么就会调用scheduleNext方法,这个方法里面从ArrayDeque里面取出一个Runnable,然后放到了THREAD_POOL_EXECUTOR这个线程池里面去执行。在Runnable执行完毕后,无论如何都会在finally里面调用scheduleNext方法,这样假如我们队列里面有很多Runnable的话,就会串行执行下去。

        • 我们再重新看一下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执行的代码参考如下:再次贴一遍代码

        • 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        }
           
          当然我们的mFuture是实现了Runnable接口的,所以肯定会重写run方法的,大家可以大胆的猜测一下,mFuture的run方法里面一定会调用mWorker的call方法,不然它怎么办~废话少说,看一下mFuture类中重写的的run方法~

        • public void run() {
          166        sync.innerRun();
          167    }
          

          继续跟进~

        • 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        }
          大家在334行看到没有,果然调用了mWorker的call方法,哈哈,说明我们的猜测完全正确~而call方法里面就是doInBackground的实现了,以后的逻辑就完全联系起来了。AsyncTask的整个过程就分析完了。

        • 分析是分析完了,但是还有几个小小的疑问。

        • 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方法。

        • public static Looper myLooper() {
          162        return sThreadLocal.get();
          163    }
          发现有一个sThreadLocal,就是我们当前创建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    }
          此时的范型T就是我们的Looper对象,我们就不再继续往里面跟了。发现最里面其实就是一个Map,我们可以根据我们的Thread来得到我们的Looper,对于没有Looper的Thread肯定是得不到Looper的。得不到Looper是不可能成功的创建Handler的,一创建就会报错~

        • 具体的关于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方法里面的实现~

        • public static void init() {
                  sHandler.getLooper();
              }
          在主线程中创建的AsyTask得到的是主线程的Looper,这也就是为什么我们直接在没有Looper的子线程中创建AsyncTask的对象不会崩溃的原因。但是4.0一下的代码不保证AsyncTask一定是得到的是主线程的Looper,所以我们还是尽量不要在子线程中创建AsyncTask的对象。

        • 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开绿灯了?

















  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值