AsyncTask详解

      上篇介绍了Handler实现主线程和子线程间的通信,在Android中还提供了一种更方便使用的AsyncTask来实现主线程和子线程通信.使用AsyncTask就不用自己去开启子线程创建Handler、重写handlerMessage()等操作了,我们只需要一个AsyncTask类即可搞定。但是在使用场合上,它适用于操作时间短的场合(最多几秒钟),如果是长时间的操作不建议使用AsyncTask,而是推荐使用Java的Executor、ThreadPoolExecutor和FutureTask等类来协助Handler来完成,这三个类都是jdk的java.util.concurrent包下的类。先看下AsyncTask的使用方法:

	private class UITask extends AsyncTask<String, Void, String> {
		private String name;

		public UITask(String name) {
			super();
			this.name = name;
		}

		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			String str = mContentView.getText().toString();
			mContentView.setText(str + "\n" + name);
		}

		@Override
		protected String doInBackground(String... params) {
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return params[0];
		}

		@Override
		protected void onPostExecute(String result) {
			super.onPostExecute(result);
			String str = mContentView.getText().toString();
			mContentView.setText(str + "\n" + result);
		}
	}
      在使用的时候直接new一个UITask对象然后调用execute(...)即可。虽然AsyncTask用起来很简单,但是需要遵循以下几个原则:

     (1)AsyncTask必须在UI线程中加载

     (2)AsyncTask实例必须在UI线程中创建

     (3)execute(...)方法必须在UI线程中调用

     (4)绝对不要去调用onPreExecute()、DoInBackground()、onPostExecute()方法

     (5)每个AsyncTask实例只能被执行一次,如果多次执行就会抛出异常

      那么AsyncTask是如何工作的呢?在使用AsyncTask请求任务时,通常调用execute(...)方法,此方法最终会调用executeOnExecutor()方法,源码如下:

     

    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
     //如果该任务正在运行或者已经结束就抛出异常
     if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
            }
        }

        mStatus = Status.RUNNING;
       //执行onPreExecute(),可以看出来,此方法在主线程直接调用到了,并不在子线程中
        onPreExecute();
       //赋值参数,mWorker是一个实现了java.util.concurrent.Callable<Result>接口的WorkerRunnable实例,
      //在WorkerRunnable类中只有一个Params[] mParams;成员变量,其他什么都没有。
         mWorker.mParams = params;
      //Executor开始执行,注意,exec默认的是一个sDefaultExecutor对象,该对象是实现Exector接口的SerialExecutor实例
      exec.execute(mFuture);
      //返回当前AsyncTask的实例
        return this;
    }
 
     这里我们要注意,当我们调用execute()方法时,AsyncTask在执行任务时会被加一个规范,这个规范就是:所有的任务都是同步的,只有当一个任务执行完毕后下一个任务才可以执行。因为此时这个exec是一个SerialExecutor实例,它是一个顺序的执行者,看下他实现的SerialExecutor源码:

    private static class SerialExecutor implements Executor {
     //ArrayDeque双向队列
     final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;
     //最终执行的就是此方法,
        public synchronized void execute(final Runnable r) {
          //添加一个Runnable对象到队列中
           mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
    //然后调用scheduleNext(),
     if (mActive == null) {
                scheduleNext();
            }
        }
       //THREAD_POOL_EXECUTOR是一个ThreadPoolExecutor线程池,会执行FutureTask任务,者流先取出一个Runnable对象,然后去执行,就这样它就保证了任务的顺序执行
        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

       此外,在executeOnExecutor()方法中我们还看到了一个mFuture对象,此对象就是一个FutureTask实例,我们看下它在AsyncTask的构造方法中被创建:

    public AsyncTask() {
   //创建WorkerRunnable对象,
    mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
     //返回DoInBackground()方法执行的结果
      return postResult(doInBackground(mParams));
            }
        };
    //创建FutureTask实例,
        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }

         可以看到,在WorkerRunnable的call()方法中,当doInBackground()方法执行完毕后会将Result参数传递给postResult()方法,并执行它,那么看下postResult()方法做了什么事情:

   

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        //用Handler取得一个Message对象,并将Result赋值给obj
        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        //发送一个消息出去给handlerMessage()处理,
        message.sendToTarget();
        return result;
    }

      在postResult()方法中sHandler对象是AsyncTask的一个Handler内部类,源码如下:

   

    private static class InternalHandler extends Handler {
        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult result = (AsyncTaskResult) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }
     看到在handleMessage()中,如果是已经执行完毕就调用finish(),方法,如果是正在执行就调用onProgressUpdate()方法,那么当我们的doInBackground()方法执行完毕后在onPostExecute(0方法中更新UI的方法是啥时候调用的呢?莫激动,在handleMessage()中只有两个判断调用finish()和onProgressUpdate(),既然onProgressUpdate()中没有调用onPostExecute(),那肯定在finish()中调用了,我们去看看finish()方法:

   

    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }
       没错,onPostExecute()方法就是在finish()中调用的,如果说在没有执行完就取消了,就不会执行onPostExecute()了。完了之后就将mStatus的状态设置为FINISHED。
    当然了,上面我们也说道了,当我们用execute()来执行任务时,多个任务是顺序执行的,后一个任务必须等前一个任务执行完后在执行,那有时候我们有很多任务需要同时执行,不要等待那么久该怎么办呢?不要急,上面的分析已经给出了答案,那就是我们直接调用executeOnExecutor(Executor exec, Params... params)方法,该方法的第二个参数和execute()的一致,第一个参数是指定一个执行者,在AsyncTask中已经提供了一个THREAD_POOL_EXECUTOR线程池,看下他的创建:

      

    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
     这个线程池可以一次同时执行9个任务(线程),如果还有更多的任务那就需要等待了,这些等待的任务就会放在sPoolWorkQueue阻塞队列。当然了,如果AsyncTask提供的线程池仍然不能满足你的需求,你也可以自己配置一个线程池,指定大小、阻塞队列、拒绝策略等。然后调用

      现在我们来捋一捋AsyncTask的执行流程:首先我们需要继承AsyncTask来实现自己的AsyncTask,并至少重写一个doInBackground()方法。流程如下:

     (1)创建一个AsyncTask实例,此时完成了Callable和Future的创建,并且在Callable的call()方法中完成了对doInbackgound()的调用,同时根据doInbackground()方法的返回来调用postResult()方法然后使用Handler发送消息最终将子线程执行的结果发送给主线程的onPostResult()方法中。

     (2)用户开始调用execute()或executeOnExecutor()方法,,此时线程池开始线程的执行。然后调用到了executeOnExcutor()方法,该方法就调用到SeriakExecutor的execute(mFuture)方法,注意传递了一个mFuture对象,而mFuture

由mWork构造,所有会调用到了WorkRunnable的call()方法中的doInBackground()方法,然后根据此方法返回的结果调用postResult()方法,然后获取Message,并发送消息给InternalHandler的handleMessage()方法来处理,也就是调用postResult(),从而调用到了onPostExecute()方法。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值