曾建议使用AsyncTask而不是使用Thread,但是AsyncTask似乎又有它的限制,这就要根据具体的需求情况而选择合适的工具,下面是一些建议:
改善你的设计,少用异步处理
线程的开销是非常大的,同时异步处理也容易出错,难调试,难维护,所以改善你的设计,尽可能的少用异步。对于一般性的数据库查询,少量的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的开发没有想像中那样简单,要多花心思和时间在代码上和测试上面,以确信程序是优质的。
5. 坑爹的AsyncTask内存泄漏:
AsyncTask底层虽然是封装了线程和handler,但是不可避免的出现了内存泄露的问题。
(1)AsyncTask的内存泄漏:
privateTextView mTextview;new AsyncTask<...>{
@Overrideprotected voidonPostExecute(Objecto) {
mTextview.setText("text");
}
}.execute();
乍一看好像没什么问题,但这段代码会导致内存泄露,线程有可能会超出当前Activity的生命周期之后仍然在run,因为这个时候线程已经不受控制了。Activity生命周期已经结束,需要被系统回收掉,但是AsyncTask还在持有TextView的引用,这样就导致了内存泄露。
既然你说上面的代码有问题,那我们把上面的代码改一改,如下:
privateTextView mTextview;new AsyncTask<...>{
@Overrideprotected voidonPostExecute(Objecto) {//mTextview.setText("text");
}
}.execute();
我直接注释掉,不做UI操作了,这样总不会有问题了吧。真的吗?
仔细看,这里是个内部类,由于Java内部类的特点,AsyncTask内部类会持有外部类的隐式引用。即使从代码上看我在AsyncTask里没有持有外部的任何引用,但是写在Activity里,对context仍然会有个强引用,这样如果线程超过Activity生命周期,Activity还是无法回收造成内存泄露。
(2)AsyncTask内存泄漏的解决方法:
在Activity生命周期结束前,去cancel AsyncTask,因为Activity都要销毁了,这个时候再跑线程,绘UI显然已经没什么意义了。
也就是在Activity的onDestory方法中调用AsyncTask.cancel(true)