标签:
android中实现异步任务机制有两种方式,Handler与AsyncTask,之前我们分析过Handler消息处理机制(见:android-----Handler消息处理机制),今天来学一学AsyncTask,相对于采用handler模式为每个任务创建一个新的线程,任务完成之后通过Handler实例向UI线程发送消息,AsyncTask更加的重量级,他不需要编写任务线程和Handler实例就可以完成相同的任务,但是他内部也是使用Handler来传递消息的,相当于是对Handler的封装;
从android官方文档中我们看到:
AsyncTask是抽象类,因此在使用的时候我们需要实现它,它主要有三个泛型参数:
Params:在执行AsyncTask的时候需要传入的参数,这个参数可用于在后台任务中使用;
Progress:后台执行任务的过程中如果需要在界面上显示当前进度的话,使用这个泛型来作为进度单位;
Result:当任务执行结束之后,如果需要对结果进行返回,则使用这个泛型作为返回结果的类型;
再看看我们平时主要用哪些方法:
当然平时我们用的时候都是在实现类中实现这些方法的:
onPreExecute():这个方法在后台任务执行之前被调用,用于一些界面初始化的操作,比如显示通知将要进行下载任务;
doInBackground(Params... params):这个是真正的后台方法,也就是说这个方法是在子线程中执行的,我们可以在这个方法里面进行某些耗时操作,比如从网络中加载图片等,任务执行结束就可以通过return语句将任务的执行结果返回,当然此时的AsyncTask第三个参数必须不是Void,如果是Void的话可以不返回任务执行结果,注意这个方法是不可以进行更新UI操作的(间接的说明一点这个方法里面是不会出现Handler相关操作的)如果想要更新UI操作的话,比如反馈当前任务的执行进度,可以调用publishProgress(Progress...values)来完成;
publishProgressUpdate(Progress... values):当后台执行程序调用publishProgress(Progress...values)之后,这个方法会马上被调用,方法的参数values就是后台任务传递过来的,这个方法可以对UI进行操作,也就是在随后的源码分析中,我们会在这个方法里面找到有关Handler的操作,利用values参数堆界面进行相应的更新;
onPostExecute(Result result):后台程序执行结束rteturn之后,这个方法会执行,用来进行一些界面的后处理操作,比如通知图片已经下载成功等;
接着来看看我们是怎么使用AsyncTask的:
首先构建一个继承自AsyncTask的实现类:
class PictureAsyncTask extends AsyncTask<Void, Integer, Bitmap> { @Override protected void onPreExecute() { mProgressDialog.show(); mProgressDialog.setMessage("将要下载图片"); } @Override protected Bitmap doInBackground(Void... arg0) { URL url = null; HttpURLConnection connection = null; Bitmap bitmap = null; try { url = new URL("http://image.baidu.com/search/down?tn=download&word=download&ie=utf8&fr=detail&url=http%3A%2F%2Fimg.55bbs.com%2F8%2F120%2Flic1XscumraVk.jpg&thumburl=http%3A%2F%2Fb.hiphotos.baidu.com%2Fimage%2Fh%253D200%2Fsign%3Ddb5f59020e24ab18ff16e63705fae69a%2F267f9e2f0708283896096030bf99a9014c08f18a.jpg"); connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(5*1000); connection.setReadTimeout(10*1000); int len = connection.getContentLength(); publishProgress(len); bitmap = BitmapFactory.decodeStream(connection.getInputStream()); } catch (Exception e) { e.printStackTrace(); } return bitmap; } @Override protected void onProgressUpdate(Integer... values) { mProgressDialog.setMessage("正在下载图片......,图片大小为: "+values[0]); } @Override protected void onPostExecute(Bitmap result) { mProgressDialog.setMessage("图片下载结束"); mProgressDialog.dismiss(); if(result != null) { mImageView.setImageBitmap(result); } } }
在这里我们通过加载一张图片来模拟耗时操作,AsyncTask传入的参数分别是Void,Integer,Bitmap,实现了其四个方法,在执行的开始先显示一个将要下载图片的对话框,执行过程中显示下载图片的大小,执行结束通知下载结束;
来看看布局文件:
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button" android:text="@string/hello_world" /> <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/button" android:layout_below="@+id/button" android:layout_marginTop="34dp" android:clickable="true" />
就只有一个Button与ImageView;
最后是Activity
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButton = (Button)findViewById(R.id.button); mImageView = (ImageView)findViewById(R.id.imageView); mProgressDialog = new ProgressDialog(MainActivity.this); mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { new PictureAsyncTask().execute(); } }); }可以看出AsyncTask的使用非常简单,只要new一个实例,调用其execute方法即可;
当然最后不要忘记添加访问网络的权限:<uses-permission android:name="android.permission.INTERNET"/>
看完了实例,接下来就该从源码层次看看AsyncTask到底是怎么实现的了:
我们在使用AsyncTask的时候首先是要new一个他的实例,那么闲看看AsyncTask的构造函数做了些什么事呢?
public AsyncTask() { mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked return postResult(doInBackground(mParams)); } }; 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类型的mWorker,一个是FutureTask类型的mFuture,并且在生成mFuture的时候将mWorker作为参数的,当然此时mWorker与mFuture只是存储在内存中的,稍后会用到他们;
有了AsyncTask对象之后调用execute方法就可以执行啦,所以我们有必要看看execute做了些什么
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }就这么一句话,想想都知道具体逻辑肯定得去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(); mWorker.mParams = params; exec.execute(mFuture); return this; }
首先会判断当前线程状态,如果已经处于运行状态或者已经完成,则抛出异常,如果处于挂起状态的话,则修改线程状态为运行状态,接着执行onPreExecute()方法,也就是我们自己实现的方法,随后设置mWorker的参数,接着执行exec的execute方法并且将mFuture对象传递进去了,这里的exec是一个SerialExecutor类型的对象,具体的赋值是在AsynTask的execute里面赋的sDefaultExecutor,而这个值从AsyncTask的源码中可以看到是一个SerialExecutor类型对象:
public static final Executor SERIAL_EXECUTOR = Utils.hasHoneycomb() ? new SerialExecutor() : Executors.newSingleThreadExecutor(sThreadFactory); private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;那么exec.execute(mFuture)执行的将是SerialExecutor的execute方法:
来看看SerialExecutor,他是AsyncTask的内部类:
private static class SerialExecutor implements Executor { final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); Runnable mActive; public synchronized void execute(final Runnable r) { mTasks.offer(new Runnable() { public void run() { try { r.run(); } finally { scheduleNext(); } } }); if (mActive == null) { scheduleNext(); } } protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) { THREAD_POOL_EXECUTOR.execute(mActive); } } }
可以看到SerialExecutor是使用ArrayDeque来管理Runnable对象的,如果我们一次性启动了很多任务的话,首先在第一次调用execute方法的时候会调用ArrayDeque的offer方法将传入的Runnable对象添加到队列中去,随后判断mActive是否为null,第一次运行的话肯定是null了,所以会执行scheduleNext方法,这个方法首先会去ArrayDeque里面取出队头元素赋值给mActive,接着调用THREAD_POOL_EXECUTOR去执行取出来的Runnable对象,THREAD_POOL_EXECUTOR是一个ThreadPoolExecutor类型的对象,相当于一个线程池,下一次再去对mActive做非空检查的时候,发现其为非null,那么是不是意味着将不再执行scheduleNext方法来执行下一个任务呢?答案是否定的,因为offer方法里面传入了一个匿名类,这里使用了try finally代码块,也就意味着scheduleNext在任何情况下都会得到调用,也就意味着每次任务执行完下一次任务才会执行,任务的执行顺序和插入队列的顺序一致,SerialExecutor模仿了单一线程池的效果(因为在执行execute方法的时候是有进行synchronized加锁操作的),即使我们快速启动了很多任务,同一时刻只会有一个线程处于运行状态;
我们也可以看看THREAD_POOL_EXECUTOR是怎么来的:
private static final int CORE_POOL_SIZE = 5; private static final int MAXIMUM_POOL_SIZE = 128; private static final int KEEP_ALIVE = 1; public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory, new ThreadPoolExecutor.DiscardOldestPolicy()); private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(10);
其实就是一个线程池而已,他默认设置的线程池大小是128,同一时刻能够运行5个线程,同时还提供了10个线程的阻塞队列,也就是说当线程数目超过138的时候会出问题,抛出RejectExecuteException异常,至于为什么这里能够并发执行5个线程但是真正执行的时候却是线性执行的原因在于真正执行的时候进行了synchronized加锁操作;
当然实际运用中我们也可以定制自己的线程池方法就是:
Executor exec = new ThreadPoolExecutor(15, 200, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); new PictureloadTask().executeOnExecutor(exec);也就是在真正执行AsyncTask的时候将自定义的线程池传入即可;
好了,SerialExecutor说的有点多了,接下来重点还是SerialExecutor得execute,正是从这个方法开始了子线程的所有耗时操作,execute方法传入的参数正是AsyncTask中executeOnExecutor方法中的mFuture,也就是一开始我们通过AsyncTask构造函数创建的放入内存的FutureTask对象,随后r.run()调用的就是FutureTask对象的run方法,所以我们有必要看看FutureTask对象的run方法是怎么实现的:
public void run() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
这个方法最核心的就是第12行的c.call()方法了,这里的c是Callable对象,也就是我们在创建mFuture的时候传入的参数mWorker啦,那么也就是说c.call()执行的将是mWorker.call()啦,这也就回到了我们的AsyncTask构造方法里面啦,为了方便阅读,再贴一遍AsyncTask的构造方法:
public AsyncTask() { mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked return postResult(doInBackground(mParams)); } }; 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); } } }; }
也就是执行的第3行的call方法了,这个方法的最后会执行postResult(doInBackground(mParams)),终于看到了熟悉的doInBackground,注意此时的操作是在子线程中执行的,postResult会将doInBackground最后执行的结果作为参数,那么我们看看postResult做了些什么:
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }
很简单,这里的sHandler是InternalHandler的一个实例,也就是说通过一个InternalHandler对象发送一条消息,这个消息携带了MESSGAE_POST_RESULT常量和子线程任务的执行结果AsyncTaskResult对象,想想我们的Handler消息处理机制,首先应该想到的就是这只是发出去消息了,那么具体的处理在什么地方呢?当然在InternalHandler里面啦,查看InternalHandler源码找到handleMessage方法:
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; } } }
他也是AsyncTask的内部类,可以发现我们刚刚消息携带的what字段是MESSGAE_POST_RESULT,也就是会执行finish方法,如果消息what字段是MESSGAE_POST_PROGRESS的话,则会执行onProgressUpdate方法,我们
先来看看finish方法:
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
finish方法首先会检查当前任务有没有被取消,如果被取消的话,则会执行onCancelled方法,没有被取消的话,则会执行我们熟悉的onPostExecute方法,这样当前任务就结束了;
接着我们来看看什么情况下会发出what字段为MESSGAE_POST_PROGRESS的消息呢?没错,从上面的实例中,我们使用了publishProgress方法,我们来看看这个方法的源码就知道了:
protected final void publishProgress(Progress... values) { if (!isCancelled()) { sHandler.obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } }
很简单,就是通过InternalHandler发出一个带有what字段为MESSGAE_POST_PROGRESS的消息,然后具体的处理还是见上面InternalHandler的handleMessage方法,调用当前AsyncTask的onProgressUpdate,而这个方法的具体实现也就是我们自己的事了;
好了,整个AsyncTask的源码分析完毕,有什么错误的地方希望大家多多指教;
标签:
原文地址:http://blog.csdn.net/hzw19920329/article/details/51338588