文档章节

AsyncTask的使用及原理分析

abcijkxyz
 abcijkxyz
发布于 2016/07/30 17:24
字数 1857
阅读 0
收藏 0
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两种线程池分析和总结 》

本文转载自:http://blog.csdn.net/wdong_love_cl/article/details/51550353

共有 人打赏支持
abcijkxyz
粉丝 64
博文 6196
码字总数 1876
作品 0
深圳
项目经理
AsyncTask 的实现原理

在《Android异步处理二:使用AsyncTask异步更新UI界面》一文中,我们介绍了如何使用AsyncTask实现异步下载图片,并且更新图片到UI界面的方法。本篇我们将学习Framework层AsyncTask的实现原理...

鉴客
2011/09/17
7.5K
1
Android多线程任务优化1:探讨AsyncTask的缺陷

导语:在开发Android应用的过程中,我们需要时刻注意保障应用的稳定性和界面响应性,因为不稳定或者响应速度慢的应用将会给用户带来非常差的交互体验。在越来越讲究用户体验的大环境下,用户...

Dean83
2012/06/14
0
0
AsyncTask的缺陷

导语:在开发Android应用的过程中,我们需要时刻注意保障应用的稳定性和界面响应性,因为不稳定或者响应速度慢的应用将会给用户带来非常差的交互体验。在越来越讲究用户体验的大环境下,用户...

鉴客
2011/09/17
49.5K
22
从源码的角度认识AsyncTask

一、为什么需要工作者线程 我们知道,Android应用的主线程(UI 线程)肩负着绘制用户界面和及时响应用户操作的重任,为了避免“用户点击按钮后没反应”这样的糟糕用户体验,我们就要确保主线...

xingjm8511
2016/06/23
11
0
Android开发——子线程操作UI的几种方法

在Android项目中经常有碰到这样的问题,在子线程中完成耗时操作之后要更新UI,下面就自己经历的一些项目总结一下更新的方法: 在看方法之前需要了解一下Android中的消息机制。 转载请标明出处...

SEU_Calvin
2016/08/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Laravel UNION联合查询并分页

$xk = DB::table('xiaokes')->select(['id','xk_name as name','xk_teacher_id as tid','ke_type','created_at']) ->where('xk_teacher_id',$id); $query = DB::......

一只懒猫-
31分钟前
1
0
187 修改centos的密码

先登录

Lucky_Me
38分钟前
3
0
通过Timer类实现App 启动页停留三秒效果

Timer timer = new Timer(); //参数1:每次执行的任务代码 参数2:延时多少毫秒执行 参数3:任务间隔 单位:毫秒 timer.schedule(new TimerTask() { @Over...

lanyu96
44分钟前
0
0
pip更改下载源设置

任何一个信息都可以把人分为两类,知道的和不知道的。有些我们已知的信息,自己觉得很平常,却可能对另一些不知道的人来说很有用处。 比如今天要说的这个小技巧,我自己原以为不值一提,网上...

crossin
44分钟前
1
0
css预加载

css /* 预加载图片,挂在body上 */.preload-img:after{ content:""; background: url(../images/new/full_bg2.jpg) no-repeat -10000px -1000px;} js $(function(){ /......

红羊在厦门
52分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部