介绍
android UI是线程不安全的,因此想实现在子线程中刷新UI就需要借助一些机制来实现,一般有两种方法:handler+message;还有一种就是今天我们要讲的asyncTask。asyncTask相对handler+message要轻量级一些。
基本用法
asyncTask是一个抽象类,因此我们需要自定义一个类来继承他,并且实现他的方法。
asyncTask指定了三个泛型参数,其中Params是可变长的泛型参数,三个参数用法如下:
Params:asyncTask传入的参数可在后台执行任务时使用
Progress:后台执行任务时,如果需要在前台显示进度,这里是指定泛型作为进度单位
Result:后台执行任务完成,如果需要返回结果到前台,这里是指定泛型作为返回值类型
除了参数下面介绍下我们经常需要重写的四个方法:
onPreExecute():在任务执行之前调用,进行一些初始化操作,比如显示一个进度条,这个方法是在主线程运行的。
doInBackground(Void... arg0):这个方法是用来执行耗时操作的,把你想要异步处理的任务的代码放在这里面,这个肯定是在子线程执行的。所以这个方法里面不能进行UI操作,如果想要更新UI的话,比如更新进度可以调用publishProgress(values)方法。
onProgressUpdate(Integer... values):后台执行了publishProgress(values);方法后处过来的值就可以用来更新前台的进度了,可见这个是在主线程执行的。
onPostExecute(Boolean result):后台任务执行完毕返回结果的时候会调用这个方法,将结果返回给主进程或者进行UI操作,可见也是在主进程进行的。
还有一个onCancelled()方法,当用户取消任务的时候调用。有两点需要注意的地方:
1、asyncTask的实例化操作和execute()方法调用必须在UI线程操作。
2、asyncTask只能被执行一次,不能重复执行。源码里有一个判断:如果任务正在运行或者是已经结束的任务重复调用会抛出异常。
下面我们来自定义一个asyncTask:
public class MyAsyncTask extends AsyncTask<Void, Integer, Boolean>{
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
}
@Override
protected Boolean doInBackground(Void... arg0) {
// TODO Auto-generated method stub
publishProgress(values);
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(Boolean result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
}
@Override
protected void onCancelled() {
// TODO Auto-generated method stub
super.onCancelled();
}
}
这里我们把AsyncTask的第一个泛型参数指定为Void,表示在执行AsyncTask的时候不需要传入参数给后台任务,第二个泛型参数指定为Integer,表示使用整型数据来作为进度显示单位。第三个泛型参数指定为Boolean,表示子线程中返回的数据类型。
Android 3.0以前AsyncTask允许同时执行5个任务,线程池大小为128个,也就是说添加了10个任务,只有前五个在执行,等有一个执行完毕才会执行第六个,当然最多添加128个任务;3.0之后改成了只允许同时执行一个任务,为什么变少了呢?3.0以后增加了他的灵活性,AsyncTask可以接受自定义线程池了。
public ExecutorService executorService = Executors.newFixedThreadPool(5);
new MyAsyncTask().executeOnExecutor(executorService);
以上代码自定义了一个线程数为5的线程池,同样是可以执行5个任务。
线程池
说道线程池,以前还真没接触过,只是用用别人封装好了的罢了。正好遇到了,也就了解一下,毕竟技不压身,哈哈哈...
先来看下ExecutorService,ExecutorService是线程池的一个服务,可以随时关闭线程池,它里面有几个常用的线程池对象:
newCachedThreadPool()
它是一个缓存型的池子,需要创建线程的时候他会先查看有没有以前创建过的,如果有就回收,没有才会重新创建。适合执行一些生命周期较短的异步任务,它里面的线程都有一个time out限制,也就是说如果一个线程超过60s没有活动,那么这个线程将被移除。当然前面说的回收,肯定是回收的没有time out 的线程。所以放入它里面的线程不必担心它的结束,超过60S不活动,会自动终止。
newFixedThreadPool(count)
这个线程池跟cache池类似,它也可以回收前面的线程,但是它的特殊之处在于同一时间它只允许固定数目的线程活动,实例化的时候可以限定一个线程数目,比如上面提到的5个线程同步执行,当然也可以根据CPU个数来动态设定线程个数,最好设置成核数的2N如下代码:
int count = Runtime.getRuntime().availableProcessors() ;
ExecutorService executorService = Executors.newFixedThreadPool(count*2);
如果指定了线程数目,在手机性能较差的情况下可能会发生卡顿,ANR现象。fixed池子里面的线程应该没有time out或者是时间很长,基本不会超时。
newScheduledThreadPool(count)
可以实现一个定长的可延时或者周期执行的线程池;
newSingleThreadExceoutor()
可以实现一个只允许一个工作线程工作的线程池。这个池子里面只允许一个线程在活动,顺序完成所有的任务。可以用于数据库的操作,文件操作,或者是批量安装应用,卸载应用等不适合高并发但有可能IO阻塞以及影响UI相应的操作。
关于多线程就讲到这里,只是讲了一点基础知识,想要深入了解的可以自行百度。
来源:oschina
链接:https://my.oschina.net/u/1589792/blog/662360