在我的《我的Android进阶之旅------>android异步加载图片显示,并且对图片进行缓存实例》文章中,先后使用了Handler和AsyncTask两种方式实现异步任务机制。
下面先来看一段代码,这段代码是用来显示条目时候调用的方法。
@Override public View getView(int position, View convertView, ViewGroup parent) { ImageView imageView = null; TextView textView = null; if (convertView == null) { convertView = layoutInflater.inflate(listviewItem, null); imageView = (ImageView) convertView.findViewById(R.id.imageView); textView = (TextView) convertView.findViewById(R.id.textView); convertView.setTag(new DataWrapper(imageView, textView));//将内容包装起来以备以后使用 } else { DataWrapper dataWrapper=(DataWrapper) convertView.getTag();//将包装类取出来 //从包装类中取数据 imageView=dataWrapper.getImageView(); textView=dataWrapper.getTextView(); } Contact contact=data.get(position); textView.setText(contact.getName()); /**异步加载图片文件*/ asynchImageLoad(imageView,contact.getImage()); return convertView; }
一开始asychImageLoad方法是使用Handler+Thread来实现的,代码如下所示:
private void asynchImageLoad(final ImageView imageView, final String imagePath) { final Handler handler=new Handler(){ @Override public void handleMessage(Message msg) {//运行在主线程中 Uri uri=(Uri) msg.obj; if (uri!=null&&imageView!=null) { imageView.setImageURI(uri); } } }; Runnable runnable=new Runnable() { @Override public void run() { try { Uri uri=ContactService.getImage(imagePath, cache); handler.sendMessage(handler.obtainMessage(10,uri)); } catch (Exception e) { e.printStackTrace(); } } }; new Thread(runnable).start(); }
由于使用Handler实现的时候,因为每次显示一个条目的时候都会新建一个线程,因此如果直接从第一个条目拉到第一千个条目的时候,就会新建一千个线程,性能开销太大。
因此我采用了AsyncTask来代替Handler+Thread方式来实现异步任务机制,其实AsyncTask是对Handler+Thread进行了良好的封装,并且加入了线程池技术,有效的降低了线程创建数量及限定了同时运行的线程数。实现代码如下所示:
/**异步加载图片文件*/ private void asynchImageLoad(ImageView imageView, String imagePath) { AsycImageTask asycImageTask=new AsycImageTask(imageView); asycImageTask.execute(imagePath); } /** * 使用AsyncTask提高性能 * 可选方法: 1, onprogressupdate(progress…) 可以使用进度条增加用户体验度。此方法在主线程执行,用户显示任务执行的进度。 2, onpreExecute() 这里是最新用户调用excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。 3, onCancelled() 用户调用取消时,要做的操作。 AsyncTask<Params, Progress, Result> AsyscTask定义了三种泛型类型params,progress和result. 1, params启动任务执行的输入参数,比如http请求的URL 2, progress后台任务执行的百分比 3, result后台执行任务最终返回的结果,比如String,比如我需要得到的list。 使用AsyncTask类,遵守的准则:1, Task的实例必须在UI thread中创建;2, Execute方法必须在UI thread中调用 3, 不要手动的调用onPfreexecute(),onPostExecute(result)Doinbackground(params…),onProgressupdate(progress…)这几个方法; 4, 该task只能被执行一次,否则多次调用时将会出现异常; AsyncTask的整个调用过程都是从execute方法开始的,一旦在主线程中调用execute方法,就可以通过onpreExecute方法, 这是一个预处理方法,比如可以在这里开始一个进度框,同样也可以通过onprogressupdate方法给用户一个进度条的显示,增加用户体验; 最后通过onpostexecute方法,相当于handler处理UI的方式,在这里可以使用在doinbackground得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回 */ private final class AsycImageTask extends AsyncTask<String, Integer, Uri>{ private ImageView imageView; public AsycImageTask(ImageView imageView) { this.imageView=imageView; } /** * 后台执行,比较耗时的操作都可以放在这里。 注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作 ,通常需要较长的时间。在执行过程中可以调用 Public progress(progress…)来更新任务的进度。 */ @Override protected Uri doInBackground(String... params) {//子线程中执行 try { return ContactService.getImage(params[0], cache); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 相当于handler处理UI的方式,在这里可以使用在doinbackground得到的结果 * 处理操作UI。此方法在主线程执行,任务执行的结果作为此方法的参数返回。 */ @Override protected void onPostExecute(Uri result) {//运行在主线程 if (result!=null&&imageView!=null) { imageView.setImageURI(result); } } }
==============================下面我们来分析AsyncTask源码=====================================
1、我们来看一下AsyncTask的大纲视图
查看一下AsyncTask的定义,如下所示:
public abstract class AsyncTask<Params, Progress, Result>
其中 Params指定了doInBackground()方法的输入参数类型,代表”启动任务执行的输入参数“
Progress指定了onProgressUpdate()方法输入参数类型,代表“后台任务执行的进度”
Result指定了onPostExecute()方法输入参数的类型和doInBackground()方法返回值的类型,代表“后台计算结果的类型”
在特定场合下,并不是所有类型都被使用,如果没有被使用,可以用java.lang.Void类型代替。
2、查看AsyncTask任务执行的方法 exectute方法,因为执行一个异步任务都需要在代码中调用此方法,触发异步任务的执行。
public final AsyncTask<Params, Progress, Result> execute(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; sExecutor.execute(mFuture); return this; }通过以上代码可以发现,首先执行的是onPreExecute()方法,该方法在UI线程中运行,可以在该方法中做一些准备工作,如初始化进度条的最大值等。
上段代码中还涉及到一下几个变量:mStatus、mWorker、sExecutor、mFuture。
(1)、关于mStatus,通过以下代码可以发现可以发现Status是个枚举类,而mStatus代表AsyncTask的状态。
AsyncTask的初始状态为PENDING,代表待定状态,
RUNNING代表执行状态,
FINISHED代表结束状态,
这几种状态在AsyncTask一次生命周期内的很多地方被使用,非常重要。
private volatile Status mStatus = Status.PENDING; /** * Indicates the current status of the task. Each status will be set only once * during the lifetime of a task. */ public enum Status { /** * Indicates that the task has not been executed yet. */ PENDING, /** * Indicates that the task is running. */ RUNNING, /** * Indicates that [email protected] AsyncTask#onPostExecute} has finished. */ FINISHED, }
(2)、关于sExecutor,通过一下代码可以发现sExecutor是一个线程池,通过分析该线程池的构造方法可以看出来:
该线程池中的线程数量是CORE_POOL_SIZE=5;
该线程池所允许的最大数量是MAXMUM_POOL_SIZE=128;
该线程中激活的线程数量是KEEP_ALIVE=10;
Keep_Alive_Time的时间单位是TimeUnit.Seconds;执行前保持任务的队列是sWorkQueue;
创建新线程时使用的工厂是sThreadFactory。
private static final int CORE_POOL_SIZE = 5; private static final int MAXIMUM_POOL_SIZE = 128; private static final int KEEP_ALIVE = 10; private static final BlockingQueue<Runnable> sWorkQueue = new LinkedBlockingQueue<Runnable>(10); private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) { return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); } }; private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
(3)、关于mWorker,实际上是AsyncTask的一个的抽象内部类的实现对象实例。
它实现了Callable<Result>接口中的call()方法,在call()方法中调用了doInBackground()方法,
而doInBackgroud()方法运行在子线程中,负责执行耗时操作。相关代码如下:
mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); return doInBackground(mParams); } };
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; }
(4)、关于mFuture,相关代码如下:
mFuture = new FutureTask<Result>(mWorker) { @Override protected void done() { Message message; Result result = null; try { result = 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) { message = sHandler.obtainMessage(MESSAGE_POST_CANCEL, new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null)); message.sendToTarget(); return; } catch (Throwable t) { throw new RuntimeException("An error occured while executing " + "doInBackground()", t); } message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(AsyncTask.this, result)); message.sendToTarget(); } };
通过上面代码可以发现,mFuture实例对象的done()方法中,如果捕捉到了CancellationException类型的异常,则发送一条“MESSAGE_POST_CANCEL”的消息;如果顺利执行,则发送一条“MESSAGE_POST_RESULT”的消息,而消息都与一个sHandler对象关联。
这个sHandler实例实际上是AsyncTask内部类InternalHandler的实例,而InternalHandler正是继承了Handler,下面我们来分析一下它的相关代码:
private static final int MESSAGE_POST_RESULT = 0x1; private static final int MESSAGE_POST_PROGRESS = 0x2; private static final int MESSAGE_POST_CANCEL = 0x3; private static final InternalHandler sHandler = new InternalHandler();
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; case MESSAGE_POST_CANCEL: result.mTask.onCancelled(); break; } } }
通过上面的代码可以发现,在处理消息时,
遇到“MESSAGE_POST_RESULT”时,它会调用AsyncTask中的finish()方法;
遇到“MESSAGE_POST_PROGRESS”时,它会调用AsyncTask中的onProgressUpdate()方法;
遇到“MESSAGE_POST_CANCLE”时,它会调用AsyncTask中的onCancelled()方法。
现在我们来看看finish()方法,通过查看代码可以发现原来finish()方法是负责调用onPostExecute(Result result)方法显示结果并改变任务状态为FINISHED。
private void finish(Result result) { if (isCancelled()) result = null; onPostExecute(result); mStatus = Status.FINISHED; }
==================================================================================================
作者:欧阳鹏 欢迎转载,与人分享是进步的源泉!
转载请保留原文地址:http://blog.csdn.net/ouyang_peng
==================================================================================================
附录:AsyncTask源代码:
/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package android.os;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;import java.util.concurrent.BlockingQueue;import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.ThreadFactory;import java.util.concurrent.Callable;import java.util.concurrent.FutureTask;import java.util.concurrent.ExecutionException;import java.util.concurrent.TimeoutException;import java.util.concurrent.CancellationException;import java.util.concurrent.atomic.AtomicInteger;/** * <p>AsyncTask enables proper and easy use of the UI thread. This class allows to * perform background operations and publish results on the UI thread without * having to manipulate threads and/or handlers.</p> * * <p>An asynchronous task is defined by a computation that runs on a background thread and * whose result is published on the UI thread. An asynchronous task is defined by 3 generic * types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>, * and 4 steps, called <code>begin</code>, <code>doInBackground</code>, * <code>processProgress</code> and <code>end</code>.</p> * * <h2>Usage</h2> * <p>AsyncTask must be subclassed to be used. The subclass will override at least * one method ([email protected] #doInBackground}), and most often will override a * second one ([email protected] #onPostExecute}.)</p> * * <p>Here is an example of subclassing:</p> * <pre class="prettyprint"> * 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)); * } * return totalSize; * } * * protected void onProgressUpdate(Integer... progress) { * setProgressPercent(progress[0]); * } * * protected void onPostExecute(Long result) { * showDialog("Downloaded " + result + " bytes"); * } * } * </pre> * * <p>Once created, a task is executed very simply:</p> * <pre class="prettyprint"> * new DownloadFilesTask().execute(url1, url2, url3); * </pre> * * <h2>AsyncTask's generic types</h2> * <p>The three types used by an asynchronous task are the following:</p> * <ol> * <li><code>Params</code>, the type of the parameters sent to the task upon * execution.</li> * <li><code>Progress</code>, the type of the progress units published during * the background computation.</li> * <li><code>Result</code>, the type of the result of the background * computation.</li> * </ol> * <p>Not all types are always used by an asynchronous task. To mark a type as unused, * simply use the type [email protected] Void}:</p> * <pre> * private class MyTask extends AsyncTask<Void, Void, Void> { ... } * </pre> * * <h2>The 4 steps</h2> * <p>When an asynchronous task is executed, the task goes through 4 steps:</p> * <ol> * <li>[email protected] #onPreExecute()}, invoked on the UI thread immediately after the task * is executed. This step is normally used to setup the task, for instance by * showing a progress bar in the user interface.</li> * <li>[email protected] #doInBackground}, invoked on the background thread * immediately after [email protected] #onPreExecute()} finishes executing. This step is used * to perform background computation that can take a long time. The parameters * of the asynchronous task are passed to this step. The result of the computation must * be returned by this step and will be passed back to the last step. This step * can also use [email protected] #publishProgress} to publish one or more units * of progress. These values are published on the UI thread, in the * [email protected] #onProgressUpdate} step.</li> * <li>[email protected] #onProgressUpdate}, invoked on the UI thread after a * call to [email protected] #publishProgress}. The timing of the execution is * undefined. This method is used to display any form of progress in the user * interface while the background computation is still executing. For instance, * it can be used to animate a progress bar or show logs in a text field.</li> * <li>[email protected] #onPostExecute}, invoked on the UI thread after the background * computation finishes. The result of the background computation is passed to * this step as a parameter.</li> * </ol> * * <h2>Threading rules</h2> * <p>There are a few threading rules that must be followed for this class to * work properly:</p> * <ul> * <li>The task instance must be created on the UI thread.</li> * <li>[email protected] #execute} must be invoked on the UI thread.</li> * <li>Do not call [email protected] #onPreExecute()}, [email protected] #onPostExecute}, * [email protected] #doInBackground}, [email protected] #onProgressUpdate} manually.</li> * <li>The task can be executed only once (an exception will be thrown if * a second execution is attempted.)</li> * </ul> */public abstract class AsyncTask<Params, Progress, Result> { private static final String LOG_TAG = "AsyncTask"; private static final int CORE_POOL_SIZE = 5; private static final int MAXIMUM_POOL_SIZE = 128; private static final int KEEP_ALIVE = 10; private static final BlockingQueue<Runnable> sWorkQueue = new LinkedBlockingQueue<Runnable>(10); private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) { return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); } }; private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory); private static final int MESSAGE_POST_RESULT = 0x1; private static final int MESSAGE_POST_PROGRESS = 0x2; private static final int MESSAGE_POST_CANCEL = 0x3; private static final InternalHandler sHandler = new InternalHandler(); private final WorkerRunnable<Params, Result> mWorker; private final FutureTask<Result> mFuture; private volatile Status mStatus = Status.PENDING; /** * Indicates the current status of the task. Each status will be set only once * during the lifetime of a task. */ public enum Status { /** * Indicates that the task has not been executed yet. */ PENDING, /** * Indicates that the task is running. */ RUNNING, /** * Indicates that [email protected] AsyncTask#onPostExecute} has finished. */ FINISHED, } /** * Creates a new asynchronous task. This constructor must be invoked on the UI thread. */ public AsyncTask() { mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); return doInBackground(mParams); } }; mFuture = new FutureTask<Result>(mWorker) { @Override protected void done() { Message message; Result result = null; try { result = 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) { message = sHandler.obtainMessage(MESSAGE_POST_CANCEL, new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null)); message.sendToTarget(); return; } catch (Throwable t) { throw new RuntimeException("An error occured while executing " + "doInBackground()", t); } message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(AsyncTask.this, result)); message.sendToTarget(); } }; } /** * Returns the current status of this task. * * @return The current status. */ public final Status getStatus() { return mStatus; } /** * Override this method to perform a computation on a background thread. The * specified parameters are the parameters passed to [email protected] #execute} * by the caller of this task. * * This method can call [email protected] #publishProgress} to publish updates * on the UI thread. * * @param params The parameters of the task. * * @return A result, defined by the subclass of this task. * * @see #onPreExecute() * @see #onPostExecute * @see #publishProgress */ protected abstract Result doInBackground(Params... params); /** * Runs on the UI thread before [email protected] #doInBackground}. * * @see #onPostExecute * @see #doInBackground */ protected void onPreExecute() { } /** * Runs on the UI thread after [email protected] #doInBackground}. The * specified result is the value returned by [email protected] #doInBackground} * or null if the task was cancelled or an exception occured. * * @param result The result of the operation computed by [email protected] #doInBackground}. * * @see #onPreExecute * @see #doInBackground */ @SuppressWarnings({"UnusedDeclaration"}) protected void onPostExecute(Result result) { } /** * Runs on the UI thread after [email protected] #publishProgress} is invoked. * The specified values are the values passed to [email protected] #publishProgress}. * * @param values The values indicating progress. * * @see #publishProgress * @see #doInBackground */ @SuppressWarnings({"UnusedDeclaration"}) protected void onProgressUpdate(Progress... values) { } /** * Runs on the UI thread after [email protected] #cancel(boolean)} is invoked. * * @see #cancel(boolean) * @see #isCancelled() */ protected void onCancelled() { } /** * Returns <tt>true</tt> if this task was cancelled before it completed * normally. * * @return <tt>true</tt> if task was cancelled before it completed * * @see #cancel(boolean) */ public final boolean isCancelled() { return mFuture.isCancelled(); } /** * Attempts to cancel execution of this task. This attempt will * fail if the task has already completed, already been cancelled, * or could not be cancelled for some other reason. If successful, * and this task has not started when <tt>cancel</tt> is called, * this task should never run. If the task has already started, * then the <tt>mayInterruptIfRunning</tt> parameter determines * whether the thread executing this task should be interrupted in * an attempt to stop the task. * * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this * task should be interrupted; otherwise, in-progress tasks are allowed * to complete. * * @return <tt>false</tt> if the task could not be cancelled, * typically because it has already completed normally; * <tt>true</tt> otherwise * * @see #isCancelled() * @see #onCancelled() */ public final boolean cancel(boolean mayInterruptIfRunning) { return mFuture.cancel(mayInterruptIfRunning); } /** * Waits if necessary for the computation to complete, and then * retrieves its result. * * @return The computed result. * * @throws CancellationException If the computation was cancelled. * @throws ExecutionException If the computation threw an exception. * @throws InterruptedException If the current thread was interrupted * while waiting. */ public final Result get() throws InterruptedException, ExecutionException { return mFuture.get(); } /** * Waits if necessary for at most the given time for the computation * to complete, and then retrieves its result. * * @param timeout Time to wait before cancelling the operation. * @param unit The time unit for the timeout. * * @return The computed result. * * @throws CancellationException If the computation was cancelled. * @throws ExecutionException If the computation threw an exception. * @throws InterruptedException If the current thread was interrupted * while waiting. * @throws TimeoutException If the wait timed out. */ public final Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return mFuture.get(timeout, unit); } /** * Executes the task with the specified parameters. The task returns * itself (this) so that the caller can keep a reference to it. * * This method must be invoked on the UI thread. * * @param params The parameters of the task. * * @return This instance of AsyncTask. * * @throws IllegalStateException If [email protected] #getStatus()} returns either * [email protected] AsyncTask.Status#RUNNING} or [email protected] AsyncTask.Status#FINISHED}. */ public final AsyncTask<Params, Progress, Result> execute(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; sExecutor.execute(mFuture); return this; } /** * This method can be invoked from [email protected] #doInBackground} to * publish updates on the UI thread while the background computation is * still running. Each call to this method will trigger the execution of * [email protected] #onProgressUpdate} on the UI thread. * * @param values The progress values to update the UI with. * * @see #onProgressUpdate * @see #doInBackground */ protected final void publishProgress(Progress... values) { sHandler.obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } private void finish(Result result) { if (isCancelled()) result = null; onPostExecute(result); mStatus = Status.FINISHED; } 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; case MESSAGE_POST_CANCEL: result.mTask.onCancelled(); break; } } } private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; } @SuppressWarnings({"RawUseOfParameterizedType"}) private static class AsyncTaskResult<Data> { final AsyncTask mTask; final Data[] mData; AsyncTaskResult(AsyncTask task, Data... data) { mTask = task; mData = data; } }}