Android_AsyncTask学习
Posted 杨迈1949
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android_AsyncTask学习相关的知识,希望对你有一定的参考价值。
AsyncTask是一个执行在UI线程的一个类,这个类可以简单实现在后台线程中执行任务,然后将结果发布到前台。当然,你也可以用Thread和Handler来实现,所以AsyncTask的设计初衷只是一个Thread+Handler的一个帮助类,而不是一个线程框架。
如何使用
AsyncTask是一个抽象类,必须由子类实现后,才能使用。子类必须重写doInBackground(Params...)
,一般情况下,也会重写onPostExecute(Result)
一个简单实现AsyncTack的子类:
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");
调用子类:
new DownloadFilesTask().execute(url1, url2, url3);
AsyncTask声明的3种泛型定义
1.Params
,需要发送给doInBackground(Params... params)
后台线程的参数.
2.Progress
,完成进度的单位,一般写int就可以,由onProgressUpdate(T t)
来接收处理
3.Result
,返回给UI线程的结果类型,由onPostExecute(Result result)
处理
如果在使用中,并没有这些参数的传递,都可以指定为Void
,比如:
private class MyTask extends AsyncTask<Void, Void, Void> ...
AsyncTask执行的4步
当一个task执行时,下面的4步都可能被执行:
1.onPreExecute()
,UI线程中执行,在执行后台线程之前被调用,一般情况下,可以show一个progressbar给用户。
2.doInBackground(Params...)
,在第一步执行完成后执行,在后台线程中调用。这一步执行相对耗时的操作,比如请求网络,优化图片等。这一步中,可以调用publishProgress(Progress...)
方法将执行进度发送到第三步。
3.onProgressUpdate(Progress...)
,UI线程执行,在调用publishProgress(Progress...)
后执行,什么时候执行,不确定。在这个方法中,可以显示给用户执行进度。
4.onPostExecute(Result)
,UI线程执行,在doInBackground()
执行完成后调用。doInBackground()
的返回结果,会作为一个参数传给onPostExecute(Result)
。
线程规则
必须遵守的几条线程规则:
- AsyncTask类必须在UI线程中装载
- AsyncTask子类必须在UI线程中实例化
- 不要手动的调用onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...)
方法
- 一个实例,只能被执行一次,也就是说只能调用一次execute(),调第二次的时候会抛出异常
执行顺序
在android 3.0之后,task就是串行执行,也就是依次打开task1
和task2
,task1
会执行,而task2
会等task1
执行完成后,再执行.所以官方又建议,执行特别耗时的操作时,请使用java.util.concurrent
包下的类来执行,比如:Executor, ThreadPoolExecutor 和FutureTask.
源码解析
搞懂了AsyncTask基本使用原理之后,就应该看看源码,是怎么实现的了。
先从构造函数看起:
public AsyncTask()
/** mWorker是一个实现了Callable接口的对象 */
mWorker = new WorkerRunnable<Params, Result>()
public Result call() throws Exception
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
/** 执行doInBackground(),并调用postResult发送结果到UI线程 */
return postResult(doInBackground(mParams));
;
/** mFuture是一个runnable对象
1,当执行这个runnable对象时,会调用mWorker的call(),
2,当call()执行完毕后,会调用mFuture的done(),
3,mFuture的get()方法能得到mworker.call()的返回值,并且这是个阻塞方法
*/
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);
;
看一下postResultIfNotInvoked()方法
private void postResultIfNotInvoked(Result result)
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked)
postResult(result);
在call()
最后一行代码,和postResultIfNotInvoked()
中,都执行了postResult()将结果发送出去:
private Result postResult(Result result)
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
看到这里,应该很熟悉了,果然AsyncTask是Hanlder的封装,内部通知UI线程也是用的Handler方式,getHandler()最终是获取一个内部类InternalHandler:
private static class InternalHandler extends Handler
public InternalHandler()
super(Looper.getMainLooper());
@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:
/** AsyncTask的第三步 */
result.mTask.onProgressUpdate(result.mData);
break;
这个InternalHandler构造方法中绑定了UI线程的Looper,负责处理从后台线程中传递过来的Message,result.mTask为当前对象,msg.what == MESSAGE_POST_PROGRESS,就调用了task的第三步,更新UI。
private void finish(Result result)
if (isCancelled())
onCancelled(result);
else /** AsyncTask的第四步 */
onPostExecute(result);
mStatus = Status.FINISHED;
finish()方法最终调用了task的第四步方法,到这里,已经知道了第三步和第四步都是在UI线程中调用的,那倒着来看第一步和第二步何时调呢?
下面从task.execute()入手:
public final AsyncTask<Params, Progress, Result> execute(Params... params)
return executeOnExecutor(sDefaultExecutor, params);
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;
/** AsyncTask的第一步 */
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
先是判断了任务的状态,是不是pending,而后再去设置状态为running,这就是为什么一个task只能执行一次了。
然后接着直接调用了AsyncTask的第一步,这也就是为什么我们必须在UI线程中去执行execute()的原因。
接着exec.execute()会开启后台线程执行mFutrue;如果还记得前面的构造函数的mWorker,mFuture的话,就知道了,这里是去执行mWorker的call()(这里会执行第三步),然后调用postResult()使用handler发送消息给InternalHandler去处理。
到这里,差不多源码解析完了,知道了task的4步都在哪些线程中执行。下面再看看这个执行AsyncTask的线程池是怎么来的。
当直接调用execute()时,默认使用成员变量sDefaultExecutor作为线程池来执行mFuture。下面看一下sDefaultExecutor怎么实例化的:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
/**
* An @link Executor that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
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);
/**
* 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);
sDefaultExecutor最终是一个SerialExecutor对象,SerialExecutor类在javaapi文档中写过,他是一个严格的串行执行任务的类,所以前面提到的,AsyncTask是串行执行的。
最终执行mFuture的是一个ThreadPoolExecutor线程池。
ps1:以前听人说AsyncTask最多开启的任务数是有限制的,但今天看了源码,发现并没有限制数量,所以自己做了个实现,去通知开启1000个任务,发现并没有挂,而且也没有什么问题,很完美的运行下去了。所以,以后不能光听别人说,如果有疑问,还是实践是检验真理的唯一标准。
ps2:写上面实验的例子时,为了方便,我写了个匿名的内部类来实例化一个task,然后开启他,结果报了个异常:
Object[] cannot be cast to String[] in AsyncTask
很是奇怪,明明我传的就是一个String类型,为什么突然就变成了Object了。
最后在http://stackoverflow.com/questions/20455644/object-cannot-be-cast-to-void-in-asynctask上找到了答案,就是说必须用实例化后的对象去调用.execute()。
以上是关于Android_AsyncTask学习的主要内容,如果未能解决你的问题,请参考以下文章