一.主线程和子线程
主线程:主线程在Android中也叫做UI线程主线程的作用是运行四大组件以及处理它们与用户的交互。在默认情况下一个进程只有一个线程,而这个线程就是主线程。
子线程:子线程的作用就是执行耗时任务,比如网络请求,I/O操作等。从Android 3.0开始系统要求网络访问操作必须要在子线程完成,否则将会抛出异常。
二.Android中线程的形态
除了传统的Thread以外,线程还有AsyncTask、HandlerThread和IntentService这三种特殊的形态,这三者的底层实现也是线程,只不过是具有特殊的表现形式,下面来分别介绍:
1.AsyncTask
概念:AsyncTask是一种封装了Thread和Handler的轻量级异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终的结果传递给主线程并在主线程中更新UI。同时AsyncTask是一个抽象的泛型类,它的定义如下:
public abstract class AsyncTask<Params,Progress,Result>
内部方法:在AsyncTask中提供了5种核心的方法,如下所示:
-
onPreExecute()
在主线程中执行,在异步任务执行之前此方法会被调用,一般可以做一些准备工作。 -
doInBackground(Params...params)
在线程池中执行,此方法用于执行异步任务,params表示异步任务的输入参数。此方法可以通过publishProgress方法来更新任务进度。此外此方法需要返回计算结果给onPostExecute方法。 -
onProgressUpdate(Progress...values)
在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用。 -
onPostExecute(Result result)
在主线程中执行,在异步任务执行之后,此方法会被调用,其中result参数是后台任务的返回值,即doInBackground方法的返回值。 -
onCancelled()
在主线程中执行,当异步任务被取消时,onCancelled()方法会被调用。当该方法被调用后onPostExecute不会被调用。
工作原理:当我们新建了一个AsyncTask对象之后,就可以使用execute方法来进行执行,同时在execute中又会去调用executeOnExecutor方法,在该方法中会去调用onPreExecute()方法,接着会去开启一个sDefaultExecutor的串行的线程池,在线程池中会去调用mWorker的call方法,而在call方法中会去调用postResult方法,在postResult方法中会通过sHandler发送了一个MESSAGE_POST_RESULT的消息,当sHandler收到MESSAGE_POST_RESULT这个消息后会调用AsyncTask的finish方法,该方法的具体实现如下:
private void finish(Result result){
if(isCancelled()){
onCancelled(result);
}
else{
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
2.HandlerThread
概念:HandlerThread继承了Thread,它是一种可以使用Handler的Thread,在它的内部会进行Looper的创建以及开启。
内部实现:Handler的实现也很简单,就是在run方法中通过Looper.prepared()来创建消息队列,并通过Looper.loop()来开启消息循环,具体实现如下:
public void run(){
mTid = Process.myTid();
Looper.prepare();
synchronized(this){
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
3.IntentService
概念:IntentService是一种特殊的Service,它继承了Service并且是一个抽象类。IntentService可用于执行后台耗时的任务,同时由于IntentService是一个服务的原因使得它的优先级比单纯的线程要高出很多。
工作流程:当IntentService被第一次启用时,它的onCreate()方法会被调用,在onCreate()方法中会创建一个HandlerThread。每次启动IntentService,它的onStartCommand方法就会被调用一次,IntentService在onStartCommand中处理每个后台任务Intent,onStartCommand()方法会调用onStart(),具体如下:
public void onStart(Intent intent,int startId){
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
在onStart()方法中会给mServiceHandler发送一条消息,当mServiceHandler接受到消息之后就会将Intent对象传递给onHandleIntent方法去处理,ServiceHandler的实现如下:
private final class ServiceHandler extends Handler{
public ServiceHandler(Looper looper){
super(looper);
}
@Override
public void handleMessage(Message msg){
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1); //执行结束,停止服务
}
}
上述代码中onHandleIntent是一个抽象方法,它需要我们在子类中去实现,它的作用是从Intent参数中区分具体的任务并去执行这些任务。关于stopSelf方法,它会在最后一个任务执行完成之后才会去停止服务。
三.Android中的线程池
概念:线程池简单来说就是会缓存一定数量的线程,通过线程池就可以避免因为频繁创建和销毁线程所带来的系统开销。Android中线程池的概念来源于Java中的Executor,它是一个接口,它的真正实现是ThreadPoolExecutor,ThreadPoolExecutor提供了一系列参数来配置线程池,通过不同的参数可以创建不同的线程池。
1.ThreadPoolExecutor
构造方法:ThreadPoolExecutor的构造方法提供了一系列参数来配置线程池,它的构造方法定义如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
-
corePoolSize
线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态。如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么当延时超过keepLiveTime之后,核心线程就会被终止。 -
maximumPoolSize
线程池所能容纳的最大的线程数,当活动线程数达到这个数值之后,后续的任务将会被阻塞。 -
keepLiveTime
默认为非核心线程闲置超时时长,超过这个时长,非核心线程就会被回收。 -
unit
用于指定keepLiveTime参数的时间单位,这是一个枚举。 -
workQueue
线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。 -
threadFactory
线程工厂,为线程池提供创建新线程的功能。ThreadFactory是一个接口,它只有一个方法:Thread newThread(Runnable r);
2.ThreadPoolExecutor执行规则
(1)如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
(2)如果线程池中的线程数量达到核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
(3)如果在(2)中无法将任务插入到任务队列中,这时就是任务队列已满,这时如果线程数量未达到线程池所规定的最大值,那么就会启用一个非核心线程来执行任务。
(4)如果线程数量已经达到了线程规定池规定的最大值,那么就会拒绝执行此任务。
3.线程池的分类
1.FixedThreadPool
它是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收。当所有的线程都处于活动状态时,新任务都会处于等待状态。FixedThreadPool只有核心线程并且这些核心线程都不会被回收,因此它能够更快的相应外界的请求。
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(nThreads,nThreads,OL,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
2.CachedThreadPool
该类线程是一种数量不定的线程池,它只有非核心线程,而它内部的最大线程数可以近似于任意大,而它内部对空闲线程有超时机制,并且超时时长为60s,超过时间线程就会被收回。由于这些特性,可以得到该线程内部的任务队列其实相当于一个空集合,这将导致任何任务都会被立即执行。该类线程比较适合执行大量的耗时较少的任务。
public static ExecutorService new CachedThreadPool(){
return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}
3.ScheduleThreadPool
该类线程的核心数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收。该线程池主要用于执行定时任务和具有固定周期的重复任务。
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
该类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行。SingleThreadExecutor的意义在于统一所有外界任务到一个线程中,这使得在这些任务之间不需要处理线程同步的问题。
public static ExecutorService newSingleThreadExecutor(){
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1,1,
OL,TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
网友评论