线程分为主线程和子线程,主线程主要处理和界面相关的事情,而子线程则用于执行耗时操作。
android找那个线程有很多种实现方式:AsyncTask、IntentService、HandlerThread。
AsyncTask封装了线程池和Handler,主要为了方便开发者在子线程中更新UI
HandlerThread是具有消息循环的线程,内部可以使用Handler
IntentService是一个服务,内部采用HandlerThread执行任务,任务执行完毕后会自动退出
一、主线程和子线程
Android沿用了java的线程模型,分为主线程和子线程,其中主线程也叫UI线程。主线程的作用是运行四大组件以及处理他们和用户的交互,而子线程则执行耗时任务,比如网络请求。I/O操作等。从android3.0开始系统要求网络访问必须在子线程中进行,否则会抛出NetworkOnMainThreadException异常,这样做是为了避免主线程由于被耗时操作阻塞而出现的ANR。
二、Android中的线程形态
1、AsyncTask:
AsyncTask是一种轻量级的异步任务类,可以在线程池中执行后台任务,然后把执行的进度和结果传递给主线程并在主线程中更新UI。AsyncTask封装了Thread和Handler,通过AsyncTask可以方便的执行后台任务以及在主线程中访问UI,但是AsyncTask不适合进行特别耗时的后台任务,特别耗时的任务建议使用线程池。
AsyncTask是一个抽象的泛型类,提供了Params、Progress和Result三个泛型参数,Params表示参数类型,Progress表示后台任务的执行进度的类型,Result表示后台任务的返回结果的类型
2、AsyncTask提供4个核心方法:
(1)onPreExecute():在主线程执行,在异步任务执行之前会被调用,可以做一些准备工作。
(2)doInBackground(Params...params):在线程池中执行,用于执行异步任务,params表示异步任务的输入参数。可以通过publishProgress方法更新任务的进度,publishProgress方法会调用onProgressUpdate方法,此方法需要返回计算结果给onPostExecute方法
(3)onProgressUpdate(Progress...values):在主线程执行,当后台任务的执行进度发生改变时调用此方法
(4)onPostExecute(Result result):在主线程中执行,在异步任务执行之后会被调用,result参数是后台任务的返回值,即doInBackground的返回值。
代码实例:
private class DownloadFilesTask extends AsyncTask<URL,Integer,Long>{
protected Long doInBackground(URL...urls){
int count = urls.length;
long totalSize = 0;
for(int i=0;i<count;i++){
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int)((i / (float) count) *100));
if(isCancelled()){
break;
}
return totalSize;
}
}
protected void onProgressUpdate(Integer...progress){
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result){
showDialog("Downloaded"+result+"bytes");
}
}
3、AsyncTask在使用过程中有一些条件限制,主要有以下几点:
(1)AsyncTask的类必须在主线程中加载。
(2)AsyncTask的对象必须在主线程中创建
(3)execute方法必须在UI线程调用
(4)不要在程序中直接调用onPreExecute、onPostExecute、doInBackground和onProgressUpdate方法
(5)一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常
4、AsyncTask的工作原理
从execute开始分析,execute会调用executeOnExecutor方法,代码如下:
public final AsyncTask<Params,Progress,Result> execute(Params...params){
return executeOnExecutor(sDefaultExecutor,params);
}
public final AsyncTask<Params,Progress,Result> executeOnExecutor(Executor exec,Params...params){
if(mStatus != Status.PENDING){
switch(mStatus){
case RUNNING:
throw new IllegalStateException("Cannot execute task:the task is already running");
case FINISHED:
throw new illegalStateException("Cannot execute task:the task has already been executed");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWroker.mParams = params;
exec.execute(mFuture);
return this;
}
sDefaultExecutor实际上是一个串行的线程池,一个进程中所有的AsyncTask全部在这个串行的线程池中排队执行。在executeOnExcutor方法中,AsyncTask的onPreExecute方法最先执行,然后线程池开始执行。下面看线程池的执行过程。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
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);
}
}
}
AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler),其中线程池SerialExecutor用于任务的排队,而线程池THREAD_POOL_EXECUTOR用于真正的执行任务,InternalHandler用于将执行环境从线程池切换到主线程。
5、HandlerThread
HandlerThread继承Thread,它是一种可以使用Handler的Thread,他的实现也很简单,就是在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环,这样就运行在HandlerThread中创建Handler,HandlerThread的run方法代码如下:
public void run(){
mTid = Process.myTid();
Looper.prepare();
synchronized(this){
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
6、IntentService
IntentService是一种特殊的Service,继承了Service并且他是一个抽象类,因此必须创建他的子类才能使用。IntentService用于执行后台耗时任务,当任务执行完成会自动停止,同时它的优先级比线程高很多。IntentService封装了HandlerThread和Handler,可以从它的onCreate中看出,代码如下:
public void onCreate(){
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService["+mName+"]");
thread.start();
mServiceLooper = thread.getLopper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
当IntentService第一次启动时,oncreate会被调用并创建一个HandlerThread,然后使用它的Looper构造一个Handler对象mServiceHandler,这样通过mServiceHandler发送的消息最终都会在HandlerThread中执行。从这个角度看,IntentService也可以执行后台任务。每次启动IntentService,他的onStartCommand方法就会调用一次,IntentService在onStartCommand中处理每个后台任务的Intent。下面看一下onStartCommand如何处理外界Intent。代码如下:
public void onStart(Intent intent,int startId){
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obe = intent;
mServiceHandler.sendMessage(msg);
}
下面来看一下IntentService的工作方式,示例代码:
public class LocalIntentService extends IntentService{
private static final String TAG = "LocalIntentService";
public LocalIntentService(){
super(TAG);
}
@Override
protected void onHandleIntent(Intent intent){
String action = intent.getStringExtra("task_action");
SystemClock.sleep(3000);
if("com.ryg.action.TASK1".equals(action)){
Log.d(TAG,"handle task:"+action);
}
}
@Override
public void onDestroy(){
super.onDestroy();
}
}
三、Android中的线程池
1、线程池的优点:
(1)重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销
(2)能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
(3)能够对线程进行简单的管理,并提供定时执行以及制定间隔循环执行等功能。
Android中的线程池来源于java中的Executor,Executor是一个接口,真正的线程池的实现为ThreadPoolExecutor。ThreadPoolExecutor提供了一系列参数来配置线程池,通过不同的参数可以创建不同的线程池。
2、ThreadPoolExecutor
(1)ThreadPoolExecutor是线程池的真正实现,他的构造方法提供了一系列参数来配置线程池,下面是ThreadPoolExecutor的一个常用的构造方法
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory)
corePoolSize:线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即是处于闲置状态
maximumPoolSize:线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后续的新任务会被阻塞
keepAliveTime:非核心线程闲置时的超时时间,超过这个时长,非核心线程就会被回收。当ThreadPoolExecutor的allowThreadTimeOut属性设置为true时,keepAliveTime同样会作用于核心线程。
unit:用于指定keepAilveTime参数的时间单位,这是一个枚举,常用的有TimeUnit、MILLISECONDS、TimeUnit.SECONDS以及TimeUnit.MINUTES等。
workQueue:线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。
threadFactory:线程工厂,为线程池提供创建新线程的功能。ThreadFactory是一个接口,只有一个方法:Thread newThread(Runnable r)
(2)ThreadPoolExecutor执行任务时大致遵循如下规则:
- 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
- 如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
- 如果无法将任务插入到任务队列中,这往往由于任务队列已满,这时候如果线程数量未达到线程池规定的最大值,会立刻启动一个非核心线程来执行任务。
- 如果线程数量已经达到最大值,那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法来通知调用者
(3)ThreadPoolExecutor的参数配置在AsyncTask中有明显的体现,代码如下
private static final int CUP_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT *2 + 1;
private static final int KEEP_ALIVE = 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory(){
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r){
return new Thread(r,"AsyncTask #"+mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,KEEP_ALIVE,TimeUnit.SECONDS,sPoolWorkQueue,sThreadFactory);
AsyncTask对THREAD_POOL_EXECUTOR这个线程池进行了配置:
核心线程数等于CPU核心数+1
线程池的最大线程数为CPU核心数的2倍+1
核心线程无超时机制,非核心线程在闲置时的超时时间为1秒
任务队列的容量为128
四:线程池的分类
Android中最常见的四种具有不同功能特性的线程池,他们都直接或间接通过配置ThreadPoolExecutor来实现自己的功能特性,四种线程池分别是:FixedThreadPool、CachedThreadPool、ScheduledThreadPool以及SingleThreadExecutor。
1、FixedThreadPool:
Executors的newFixedThreadPool方法来创建。他是一种线程数量固定的线程池,当线程处于空闲状态时,他并不会被回收,除非线程池被关闭了。示例代码
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(nThreads,nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
2、CachedThreadPool:
Executors的newCachedThreadPool方法来创建。他是一种线程数量不定的线程池,他只有非核心线程,并且最大线程数为Integer.MAX_VALUE。由于Integer.MAX_VALUE是一个很大的数,实际上相当于最大线程数.
public static ExecutorService newCachedThreadPool(int nThreads){
return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.MILLISECONDS,new SynchronousQueue<Runnable>());
}
3、ScheduledThreadPool:
通过Executors的newScheduledThreadPool来创建。他的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize){
super(corePoolSize,Integer.MAX_VALUE,0,NANOSECONDS,new DelayedWorkQueue());
}
4、SingleThreadExecutor:
通过Executors的newSingleThreadExecutor方法来创建。这类线程池内部只有一个核心线程,他确保所有任务都在同一个线程中按顺序执行。SingleThreadExecutor的意义在于统一所有的外界任务到一个线程中,这使得在这些任务之间不需要处理线程同步的问题。代码如下:
public static ExecutorService newSingleThreadExecutor(){
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
除了上面系统提供的4类线程池以外,也可以根据实际需要灵活低配置线程池。示例代码:
Runnable command = new Runnable(){
@Override
public void run(){
SystemClock.sleep(2000);
}
};
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);
fixedThreadPool.execute(command);
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool.execute(command);
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);
scheduledThreadPool.schedule(command,2000,TimeUnit.MILLISECONDS);
scheduledThreadPool.scheduleAtFixedRate(command,10,1000,TimeUnit.MILLISECONDS);
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(command);