1. Handler+Thread
2. AsyncTask
3. ThreadPoolExecutor
4. IntentService
下面分别对四种方式进行介绍。
一、Handler+Thread
关于Handler机制简介可以参考这篇文章,Handler可以把一个Message对象或者Runnable对象压入到消息队列中,进而在UI线程中获取Message或者执行Runnable对象,Handler把压入消息队列有两类方式,Post和sendMessage:
1、post方式:
在线程中通过handler.post(runnable)方法,可以在runnable的run方法中执行更新UI的操作。Post允许把一个Runnable对象入队到消息队列中。它的方法有:post(Runnable)/postAtTime(Runnable,long)/postDelayed(Runnable,long)。
handler.post(new Runnable() {
@Override
public void run() {
//ui操作
}
});
查看handler的post方法,可以看到对应的方法调用逻辑,和send发送消息是一致的。
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
2、sendMessage
sendMessage允许把一个包含消息数据的Message对象压入到消息队列中。它的方法有:sendEmptyMessage(int)/sendMessage(Message)/sendMessageAtTime(Message,long)/sendMessageDelayed(Message,long)。
Handler如果使用sendMessage的方式把消息入队到消息队列中,需要传递一个Message对象,而在Handler中,需要重写handleMessage()方法,用于获取工作线程传递过来的消息,此方法运行在UI线程上。Message是一个final类,所以不可被继承。
二、AsyncTask
AsyncTask封装了线程池和Handler,是Android的一个轻量级的异步类,它可以在线程池中执行后台操作,然后把执行的进度和结果通过Handler传递给主线程并在主线程里面更新UI。可以方便开发者实现异步操作。AsyncTask是一个抽象的泛型类,提供了Params、Progress、Result三个泛型参数
public abstract class AsyncTask<Params, Progress, Result>(){}
Params:需要传入参数的类型Progress:后台任务的执行进度的类型
Result:后台任务返回结果的类型
AsyncTask提供了4个核心的方法,分别是:
1. OnPreExecute(), 在主线程中执行,在异步任务之前,此方法被调用一般做一些准备性的工作;
2. doInBackground(Params…params),在线程池中执行,用于执行异步任务,params表示异步任务的传入参数。在此方法会中调用publishProgress(progress)来更新任务进度,publishProgress()会调用onProgressupdate()更新进度,会返回计算结果给onPostExecute();
3. onProgressUpdate(Progress…value),在主线程中执行,后台执行任务的进度有变化时被调用;4. onPostExecute(Result result), 在主线程中执行,在异步任务执行之后,此方法会被调用,result为后台任务的返回值,即doInBackground()的返回值。
示例:
public class DownloadFilesTask extends AsyncTask<URL, Integer, Long>{
protected Long doInBackground(URL...url){
int count = url.length;
long totalSize = 0;
for(int i = 0; i< count; i++){
totalSize += DownloadFile(url[i]);
publishProgress((int) (i/(float)count)*1000);
if(isCancelled){
break;
}
}
}
protected void onProgressUpdate(Integer...progress){
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result){
showDialog("bytes"+result);
}
}
AsyncTask通过一个阻塞队列BlockingQuery<Runnable>存储待执行的任务,利用静态线程池THREAD_POOL_EXECUTOR提供一定数量的线程,默认128个。在Android 3.0以前,默认采取的是并行任务执行器,3.0以后改成了默认采用串行任务执行器,通过静态串行任务执行器SERIAL_EXECUTOR控制任务串行执行,循环取出任务交给THREAD_POOL_EXECUTOR中的线程执行,执行完一个,再执行下一个。
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
使用AsyncTask的注意事项:
1、AsyncTask的类必须在主线程里加载,必须在主线程中调用,execute方法必须在UI线程调用;
2、 不要在程序中直接调用onPreExecute()、onPostExecute()、doInBackground()、和onProgressUpdate();
3、 一个AsyncTask对象只能执行一次,即只能调用一次execute(),否则会报运行时异常;
在面试过程中被问到AsyncTask的缺陷,可以分为两个部分说,在3.0以前,最大支持128个线程的并发,10个任务的等待;在3.0以后,无论有多少任务,都会在其内部单线程执行。
三、ThreadPoolExecutor
ThreadPoolExecutor提供了一组线程池,可以管理多个线程并行执行。这样一方面减少了每个并行任务独自建立线程的开销,另一方面可以管理多个并发线程的公共资源,从而提高了多线程的效率。所以ThreadPoolExecutor比较适合一组任务的执行。Executors利用工厂模式对ThreadPoolExecutor进行了封装,使用起来更加方便。
我们先来看看,创建一个线程池需要哪些参数。
1、corePoolSize 核心线程数大小。当提交一个任务时,如果当前线程数小于corePoolSize,就会创建一个线程。即使其他有可用的空闲线程。
2、runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。 可以选择以下几个阻塞队列:- ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序
- LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
- SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等上一个元素被移除之后,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
- PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
3、maximumPoolSize(线程池最大大小):线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。
4、keepAliveTime 线程执行结束后,保持存活的时间。
5、ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。
6、RejectedExecutionHandler 线程池队列饱和之后的执行策略,默认是采用AbortPolicy。JDK提供四种实现方式:
- AbortPolicy:直接抛出异常
- CallerRunsPolicy :只用调用者所在线程来运行任务
- DiscardOldestPolicy 丢弃队列里最近的一个任务,并执行当前任务
- DiscardPolicy : 不处理,丢弃掉
Executors提供了四种创建ExecutorService的方法,他们的使用场景如下:
1. Executors.newFixedThreadPool(int nThreads)
创建一个固定数量的线程,参数中核心线程和最大线程一样,线程保留时间 0 ,使用 LinkedBlockingQueue 作为任务队列。如果有新的任务提交,但是没有线程可用,这个任务会一直等待直到有可用的线程。线程会一直保持,直到线程池 shutDown。由于线程不会被回收,会一直卡在阻塞,所以没有任务的情况下, FixedThreadPool 占用资源更多。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2. Executors.newCachedThreadPool()
当有任务提交进来,会优先使用线程池里可用的空闲线程来执行任务,但是如果没有可用的线程会直接创建线程。空闲的线程会保留 60s,之后才会被回收。这些特性决定了,当需要执行很多短时间的任务时,CacheThreadPool 的线程复用率比较高, 会显著的提高性能。而且线程60s后会回收,意味着即使没有任务进来,CacheThreadPool 并不会占用很多资源。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
3. Executors.newScheduledThreadPool()
创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
4. Executors.newSingleThreadExecutor()
创建一个单线程化的executor,它只创建唯一的worker线程来执行任务
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
部分示例,各线程池使用方法差不多:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
poolExecutor = new ThreadPoolExecutor(3, 5,
1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(128));
}
public void btnClick(View view) {
for (int i = 0; i < 30; i++) {
final int finalI = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
Log.d("wtf", "run: " + finalI);
}
};
poolExecutor.execute(runnable);
}
}
四、IntentService
IntentService继承自Service,是一个经过包装的轻量级的Service,用来接收并处理通过Intent传递的异步请求。客户端通过调用startService(Intent)启动一个IntentService,利用一个work线程依次处理顺序过来的请求,处理完成后自动结束Service。
一个可以处理异步任务的简单Service。在 IntentService 内有一个工作线程来处理耗时操作,当任务执行完后,IntentService 会自动停止,不需要我们去手动结束。如果启动 IntentService 多次,那么每一个耗时操作会以工作队列的方式在 IntentService 的 onHandleIntent 回调方法中执行,依次去执行,执行完自动结束。可以在onHandleIntent中执行耗时任务。
通过多次启动IntentService来执行多个耗时任务(在onHandleIntent中执行耗时任务)
Intent intent = new Intent(this,MIntentService.class);
for (int i=0;i<7;i++) {//循环启动任务
intent.putExtra(MyIntentService.DOWNLOAD_URL,url[i]);
intent.putExtra(MyIntentService.INDEX_FLAG,i);
startService(intent);
}