Android异步消息处理之AsyncTask

来源:互联网 发布:淘宝助理商品简称 编辑:程序博客网 时间:2024/04/20 17:21

首先看一下AsyncTask的基本结构
这里写图片描述
这里写图片描述
从中我们可以得到3个比较重要的信息点:
1、AsyncTask为了防止线程阻塞而用于执行简单的异步处理的类,为了与UI线程交互的,因此如果工作线程不与UI线程交互,无需使用AsyncTask,直接用Thread即可。
2、比起Handler实现异步的过程:需要使用到Handler, Looper, Message,Thread四个对象,并需要通过主线程启动Thread(子线程)运行并生成Message-àLooper获取Message并传递给HandleràHandler逐个获取Looper中的Message,并进行UI变更。AsyncTask的实现过程更为简单,只有4个步骤,分别为onpreexecute,doInBackground,onProgressUpdate和onpostexecute。
3、AsyncTask是被设计成处理异步操作的一个辅助类而不是一个通用的线程框架,应该被用于一些简单的通信异步处理的情况中(最多几秒钟),如果要保持长时间的通信线程运行(比如文件传输之类的),最好不要使用AsyncTask!
4、AsyncTask是一个抽象类,它内部维护一个静态的线程池,一个app只有这么一个线程池。因此一个app中不管有多少个AsyncTask的实例,这个线程池是所有实例共享的,如果要同时更新很多视图,可能会很卡。
你的应用中,可能有多个AsyncTask实例,而不管多少个AsyncTask,只要是调用execute()方法,都是共享这个默认进程池的,你的任务必须在之前的任务执行完以后,才能执行。
因此除了excute方法外,我们可以调用executeOnExecutor,如果使用executeOnExecutor方法,可以自定义线程池,解决不能并发执行异步任务的问题。
例如:

executeOnExecutor(Executors.newCachedThreadPool());

这样这个AsyncTask实例就有了自己的线程池而不必使用AsyncTask默认的。
5、AsyncTask一旦执行了 doInBackground,就算调用取消方法,也会将 doInBackground里面的代码执行完毕,才会停止。

串行还是并行,假设我们在一个方法体里面有如下两行代码

new AsyncTask1().execute(); new AsyncTask2().execute(); 

上面的两个任务时同时执行呢,还是AsyncTask1执行结束之后,AsyncTask2才能执行呢?实际上是结果依据API不同而不同。
在1.6(Donut)之前:
在第一版的AsyncTask,任务是串行调度。一个任务执行完成另一个才能执行。由于串行执行任务,使用多个AsyncTask可能会带来有些问题。

从1.6到2.3(Gingerbread)
后来Android团队决定让AsyncTask并行来解决1.6之前引起的问题,这个问题是解决了,新的问题又出现了。很多开发者实际上依赖于顺序执行的行为。于是很多并发的问题蜂拥而至。

3.0(Honeycomb)到现在
Android团队又把AsyncTask改成了串行。当然这一次的修改并没有完全禁止AsyncTask并行。你可以通过设置executeOnExecutor(Executor)来实现多个AsyncTask并行,Executor是自己定义的线程池, 也可以用AsyncTask类的executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)方法

为什么Android的AsyncTask不适合执行长时间操作的任务?
内存泄露问题
在Activity中作为内部类创建AsyncTask很方便。因为AsyncTask在执行完成或者执行中需要使用Activity中的view,因为内部类可以直接访问外部类的域(也就是变量)。然而这意味着内部类会持有外部类的一个引用。
当长时间运行时,因为AsyncTask持有Activity的引用,所以即使当该Activity不再显示时Android也无法释放其占用的资源。

追加:
AsyncTask新增了两个预定义的线程池SERIAL_EXECUTOR 和 THREAD_POOL_EXECUTOR。

其实 THREAD_POOL_EXECUTOR 并不是新增的,之前的就有,只不过之前(Android 2.3)它是AsyncTask私有的,未公开而已。THREAD_POOL_EXECUTOR 是一个corePoolSize为5的线程池,也就是说最多只有5个线程同时运行,超过5个的就要等待。所以如果使用 executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) 就跟2.3版本的 AsyncTask.execute() 效果是一样的。

SERIAL_EXECUTOR 是新增的,它的作用是保证任务执行的顺序,也就是它可以保证提交的任务确实是按照先后顺序执行的。它的内部有一个队列用来保存所提交的任务,保证当前只运行一个,这样就可以保证任务是完全按照顺序执行的,默认的execute()使用的就是这个,也就是 executeOnExecutor(AsyncTask.SERIAL_EXECUTOR) 与execute()是一样的。

最后引用大牛的建议:

虽然建议使用AsyncTask而不是使用Thread,但是AsyncTask似乎又有它的限制,这就要根据具体的需求情况而选择合适的工具,No Silver Bullet。下面是一些建议:

改善你的设计,少用异步处理

线程的开销是非常大的,同时异步处理也容易出错,难调试,难维护,所以改善你的设计,尽可能的少用异步。对于一般性的数据库查询,少量的I/O操作是没有必要启动线程的。

与主线程有交互时用AsyncTask,否则就用Thread

AsyncTask被设计出来的目的就是为了满足Android的特殊需求:非主线程不能操作(UI)组件,所以AsyncTask扩展Thread增强了与主线程的交互的能力。如果你的应用没有与主线程交互,那么就直接使用Thread就好了。

当有需要大量线程执行任务时,一定要创建线程池

线程的开销是非常大的,特别是创建一个新线程,否则就不必设计线程池之类的工具了。当需要大量线程执行任务时,一定要创建线程池,无论是使用AsyncTask还是Thread,因为使用AsyncTask它内部的线程池有数量限制,可能无法满足需求;使用Thread更是要线程池来管理,避免虚拟机创建大量的线程。比如从网络上批量下载图片,你不想一个一个的下,或者5个5个的下载,那么就创建一个CorePoolSize为10或者20的线程池,每次10个或者20个这样的下载,即满足了速度,又不至于耗费无用的性能开销去无限制的创建线程。

对于想要立即开始执行的异步任务,要么直接使用Thread,要么单独创建线程池提供给AsyncTask

默认的AsyncTask不一定会立即执行你的任务,除非你提供给他一个单独的线程池。如果不与主线程交互,直接创建一个Thread就可以了,虽然创建线程开销比较大,但如果这不是批量操作就没有问题。

Android的开发没有想像中那样简单,要多花心思和时间在代码上和测试上面,以确信程序是优质的

0 0
原创粉丝点击