AsyncTask的使用及原理分析

Android的AsyncTask比Handler更轻量级一些,是用来做简单的异步处理的。

使用的优点:

l  简单,快捷

l  过程可控

使用的缺点:

l  在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来。

AsyncTask定义了三种泛型类型 Params,Progress和Result。(也是可以指定为空的,如 AsyncTask <Void, Void, Void>)

  • Params 启动任务执行的输入参数。
  • Progress 后台任务执行的百分比。
  • Result 后台执行任务最终返回的结果。

一个异步加载数据最少要重写以下这两个方法:

  • doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。此方法在后台线程执行,完成任务的主要工作。在执行过程中可以调用publishProgress(Progress…)来更新任务的进度。其参数对应AsyncTask的第一个参数,publishProgress(Progress…)的参数对应AsyncTask的第二个参数,其返回值对应AsyncTask的第三个参数。
  • onPostExecute(Result)  相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的返回结果操作UI。 此方法在主线程执行,任务执行的结果(doInBackground的返回值)作为此方法的参数。

当然了,还可以重写以下这三个方法,但不是必须的:

  • onProgressUpdate(Progress…)   可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
  • onPreExecute()        调用excute()方法时执行,当任务执行之前开始调用此方法。
  • onCancelled()             用户调用取消(调用了cancel(true))时,要做的操作。

使用AsyncTask类,以下是几条必须遵守的准则:

1、Task的实例必须在UI thread中创建;

2、execute方法必须在UI thread中调用;

3、不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;

4、该task只能被执行一次,多次调用时将会出现异常;

执行的顺序是:onPreExecute(),doInBackground(),onProgressUpdate(),onPostExecute()。onCancelled()不一定会执行。

除了doInBackground是在子线程执行,其他的都是在UI线程执行的。

下面说下我们具体是怎么使用的:

第一步,创建一个异步任务的实例,并且执行,这里传入的参数对应的是AsyncTask的第一个参数:

MyTask mTask = new MyTask();
mTask.execute(url);

第二步,建立一个异步任务的类,如下:

private class MyTask extends AsyncTask<String, Integer, String> {
	//onPreExecute方法用于在执行后台任务前做一些UI操作
	@Override
	protected void onPreExecute() {
		textView.setText("start loading...");
	}  

	//doInBackground方法内部执行后台任务,不可在此方法内修改UI。这里传入的参数对应AsyncTask的第一个参数,即execute()函数传递过来的
	@Override
	protected String doInBackground(String... params) {
		... //做一些耗时的动作
		publishProgress(progresses); //progresses对应AsyncTask的第二个参数,同时也是要传给onProgressUpdate()的参数
		...
		return result; //返回值对应AsyncTask的第三个参数,同时也是要传给onPostExecute()的参数
	}  

	//onProgressUpdate方法用于更新进度信息
	@Override
	protected void onProgressUpdate(Integer... progresses) {
		textView.setText("loading..." + progresses[0] + "%");
	}  

	//onPostExecute方法用于在执行完后台任务后更新UI,显示结果
	@Override
	protected void onPostExecute(String result) {
		textView.setText(result);
	}  

	//onCancelled方法用于在取消执行中的任务时更改UI
	@Override
	protected void onCancelled() {
		textView.setText("cancelled");
	}
}

第三步,不是必须的,如果需要取消任务,可以调用:

mTask.cancle(true);

该函数一旦被调用就不会执行到onPostExecute()了,而是执行onCancelled()方法。

这么简单强大的功能是怎么实现的呢?我们跟着源码来一探究竟。

先看下AsyncTask的构建函数:

public AsyncTask() {
	mWorker = new WorkerRunnable<Params, Result>() {
		public Result call() throws Exception {
			mTaskInvoked.set(true);
			Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
			return postResult(doInBackground(mParams));
		}
	};
	mFuture = new FutureTask<Result>(mWorker) {
		@Override
		protected void done() {
			...
		}
	};
}

就是建立了WorkerRunnable和FutureTask两个实例,并把mWorker传递给了mFuture。

我们要启动某一任务都会执行下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) {
    ...
    mStatus = Status.RUNNING;
    onPreExecute();
    mWorker.mParams = params;
    exec.execute(mFuture);
    return this;
}

在这里我们看到了onPreExecute(),因此可以看出onPreExecute()是首先被执行的,然后调用了exec.execute(),从上面的代码中我们看到exec就是sDefaultExecutor,那么sDefaultExecutor是什么东东呢?我们看到了如下代码:

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
……
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

也就是我们实际调用的是SerialExecutor的execute()方法,看下实现:

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);
        }
    }
}

首先把任务放到mTasks这个集合里面;然后判断mActivie如果为空,就调用scheduleNext ()方法。

mActivie为null的意思是当前没有任务在执行,如果mActivie!=null,那么说明当前有任务正在执行,那么只要把任务添加到mTasks里面即可。

因为任务执行完毕后,会再次调用scheduleNext()方法的,就是

finally {

scheduleNext();

}

这样就形成了一种链状调用结构,只要mTasks里面还有任务,就会不断逐一调用,如果后面有任务进来,就只要添加到mTasks里面即可。

这里给execute()传递的参数是mFuture,所以会执行到mFuture的run()方法,而run()方法最终会调用如下方法:

void innerRun() {
    if (!compareAndSetState(READY, RUNNING))
        return;
    runner = Thread.currentThread();
    if (getState() == RUNNING) { // recheck after setting thread
        V result;
        try {
            result = callable.call();
        } catch (Throwable ex) {
            setException(ex);
            return;
        }
        set(result);
    } else {
        releaseShared(0); // cancel
    }
}

我们看到有调用callable.call(),而callable就是mWorker了,所以这里就执行到了下面的函数:

public Result call() throws Exception {
	mTaskInvoked.set(true);
	Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
	return postResult(doInBackground(mParams));
}

postResult(doInBackground(mParams))这一句就走到了doInBackground()方法了,并且将它的返回值传给了postResult,那我们看下postResult的具体实现吧!

private Result postResult(Result result) {
    Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

原来是使用了sHandler来发送消息,那我们再看下处理消息的地方:

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:
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

收到MESSAGE_POST_RESULT就执行finish(),收到MESSAGE_POST_PROGRESS就执行onProgressUpdate()。我们看下finish()的实现:

private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

这里做了一个判断,根据任务是否被取消来调用不同的方法。

那MESSAGE_POST_PROGRESS消息是什么时候发送的呢?猜想一下,肯定是在publishProgress()里面,看下实现:

protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}

至此AsyncTask相关的流程就讲的差不多了,可以看到AsyncTask的任务执行是单线程的,关于AsyncTask的任务执行是单线程实现还是多线程实现是经历了一番变化的,较早的版本是单线程实现,从Android2.X开始,Google把它改为多线程实现,后来发现,多线程实现的话,会有很多需要保证线程安全的额外工作留给开发者,所以从Android3.0开始,又把默认实现改为单线程了。

使用多线程实现的话,有一个很大的缺陷,那就是线程池总大小是128,也就是如果任务数量超过了128,那么程序就会崩溃,如果有兴趣可以看我下面一篇文章《Android AsyncTask两种线程池分析和总结 》

时间: 2024-10-31 22:22:15

AsyncTask的使用及原理分析的相关文章

Android异步任务AsyncTask的使用与原理分析

在上一篇文章<Android缓存机制&一个缓存框架推荐>中说到,在了解了Android缓存机制后我准备自己动手写一个LruCache和DiskLruCache二级缓存的轻量级的图片请求框架,在思考如何搭建这个框架时,纠结于用何种方式去下载图片,是直接new出一个线程呢,还是用看起来稍微高大上档次一点的AsyncTask异步任务来处理?思来想去,还是虚荣心作怪,还是用AsyncTask吧,正好这个工具类我之前用的也比较少,对它的原理也不是很清楚,趁这个机会,好好学一下AsyncTask的

AsyncTask,IntentService工作原理分析&amp;Android线程池

一,android中的主线程和子线程 android中的主线程可以认为是UI线程,在主线程不可以执行耗时的操作,否则就会给人一种卡顿的感觉.而主线程主要用于处理四大组件,以及处理它们和用户的交互.anroid的子线程的主要功能就是处理耗时操作. 要知道"在android3.0之后,要求网络访问必须在子线程执行,否则会抛出NetWorkOnMainThreadException异常." 二,Android中的线程形态 Android中的线程状态,除了传统的Thread,还包含AsyncT

android之AsyncTask原理分析

通过名字就可以知道,AsyncTask主要用于处理android中的异步任务.但是通过源码,我们可以看到它的实现其实还是依赖于Handler的异步消息处理机制.现在我们先来学习它的使用方式,然后再研究源码. 一.AsyncTask的基本用法: AsyncTask是一个抽象类,在之类继承它时,必须指定三个泛型参数,这三个参数的用途如下: 1. 在执行AsyncTask时需要传入的参数,可用于在后台任务中使用. 2. 后台任何执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位.

Handler 原理分析和使用之HandlerThread

前面已经提到过Handler的原理以及Handler的三种用法.这里做一个非常简单的一个总结: Handler 是跨线程的Message处理.负责把Message推送到MessageQueue和处理. Looper 用来轮询MessageQueue,获取Message 发送给指定的Handler进行处理. Looper 需要和线程绑定,绑定那个线程,Handler就会在那个线程处理Message 前两篇文章使用Handler处理的场景是:主线程(UI线程)被子线程更新.即使用主线程的Handle

Android中Thread、Handler、Looper、MessageQueue的原理分析

在Android开发当中,Thread.Handler.Looper这几个类是特别常见,在刚开始学习Android的时候对这些类可能并不是很清晰.下面我们就一起从源码的角度剖析一下这几个类的工作原理. Thread 首先是Thread, 我们都知道一个Thread就是一个线程对象,只要在run方法中填写自己的代码然后启动该线程就可以实现多线程操作.例如 : new Thread(){ public void run() { // 耗时的操作 }; }.start(); 我们知道,针对上面的代码中

kafka producer实例及原理分析

1.前言 首先,描述下应用场景: 假设,公司有一款游戏,需要做行为统计分析,数据的源头来自日志,由于用户行为非常多,导致日志量非常大.将日志数据插入数据库然后再进行分析,已经满足不了.最好的办法是存日志,然后通过对日志的分析,计算出有用的数据.我们采用kafka这种分布式日志系统来实现这一过程. 步骤如下: 搭建KAFKA系统运行环境 如果你还没有搭建起来,可以参考我的博客: http://zhangfengzhe.blog.51cto.com/8855103/1556650 设计数据存储格式

android脱壳之DexExtractor原理分析[zhuan]

http://www.cnblogs.com/jiaoxiake/p/6818786.html内容如下 导语: 上一篇我们分析android脱壳使用对dvmDexFileOpenPartial下断点的原理,使用这种方法脱壳的有2个缺点: 1.  需要动态调试 2.  对抗反调试方案 为了提高工作效率, 我们不希望把宝贵的时间浪费去和加固的安全工程师去做对抗.作为一个高效率的逆向分析师, 笔者是忍不了的,所以我今天给大家带来一种的新的脱壳方法——DexExtractor脱壳法. 资源地址: Dex

android脱壳之DexExtractor原理分析

导语: 上一篇我们分析android脱壳使用对dvmDexFileOpenPartial下断点的原理,使用这种方法脱壳的有2个缺点: 1.  需要动态调试 2.  对抗反调试方案 为了提高工作效率, 我们不希望把宝贵的时间浪费去和加固的安全工程师去做对抗.作为一个高效率的逆向分析师, 笔者是忍不了的,所以我今天给大家带来一种的新的脱壳方法--DexExtractor脱壳法. 资源地址: DexExtractor源码:https://github.com/bunnyblue/DexExtracto

Adaboost算法原理分析和实例+代码(简明易懂)

Adaboost算法原理分析和实例+代码(简明易懂) [尊重原创,转载请注明出处] http://blog.csdn.net/guyuealian/article/details/70995333     本人最初了解AdaBoost算法着实是花了几天时间,才明白他的基本原理.也许是自己能力有限吧,很多资料也是看得懵懵懂懂.网上找了一下关于Adaboost算法原理分析,大都是你复制我,我摘抄你,反正我也搞不清谁是原创.有些资料给出的Adaboost实例,要么是没有代码,要么省略很多步骤,让初学者