一:什么是线程
线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每个线程并行执行不同的任务。
二:线程的分类
Android中的线程分为主线程和子线程,主线程主要处理和界面相关的事情,而子线程一般用于耗时操作。由于Android的特性,如果主线程中执行耗时操作那么就会导致程序无法及时响应,因此耗时操作必须放在子线程中执行。
主线程是指进程所拥有的线程,在java中默认情况下一个进程只有一个线程,这个线程就是主线程。除了主线程以外都是子线程。
主线程的作用是运行四大组件以及处理他们和用户的交互。
子线程的作用是执行耗时任务,比如网路请求,I/O操作等。
三:Android中的线程形态
除了Thread本身外,在Android中可以扮演线程角色的还有很多,比如AsyncTask、IntentService和HandlerThread。尽管他们表现形式有别于传统线程,但是他们的本质仍然是传统的线程。
AsyncTask它的底层用到了线程池,IntentService和HandlerThread来说,它们底层直接使用了线程。
AsycnTask封装了线程池和Handler,它主要是为了方便开发者在子线程中更新UI。
HandlerThread是一种具有消息循环的线程,在它的内部可以使用Handler。
IntentService是一个服务,系统对其进行了封装,可以方便执行后台任务,IntentService内部采用HandlerThread来执行任务,当任务执行完毕后IntentService会自动退出。从任务的角度看,IntentService很像一个后台线程,但是IntentService是一种服务,它不容易被系统杀死从而尽可能保证任务的执行,而如果是一个后台线程,由于没有活动的四大组件,那么这个进程的优先级会非常低,容易被系统杀死,这就是IntentService的优点。
四:AsyncTask和它的工作原理
AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。但AsyncTask并不合适进行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池。
AsyncTask是一个抽象的泛型类,提供了Params.Progress和Result三个泛型参数,Params表示参数的类型,Progress表示后台任务的执行进度的类型,Result表示后台任务的返回结果的类型,如果AsyncTask确实不需要传递具体的参数,那么这三个泛型参数可以用void来代替。
public abstract class AsyncTask
AsyncTask提供了4个核心方法,他们的含义如下
(1)onPreExecute()在主线程中执行,在异步任务执行之前,此方法会被调用,一般用于做一些准备工作。
(2)doInBackground(Params...params)在线程池中执行,此方法用于执行异步任务,params参数表示异步任务的输入参数。在此方法中可以通过publishProgress方法来更新任务的进度,publishProgress方法会调用onProgressUpdate方法。另外此方法需要返回计算结果onPostExecute方法。
(3)onProgressUpdate(Progress...values)在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用。
(4)onPostExecute(Result result)在主线程中执行,在异步任务执行之后,此方法会被调用,其中result参数是后台任务的返回值,即doInBackground的返回值。
这几个方法onPreExecute先执行,接着是doInBackground最后才是onPostExecute。出了以上4个方法外,AsyncTask还提供了onCancelled()方法,它同样在主线程执行,当异步任务被取消时,onCancelled()方法会被调用,这个时候onPostExecute则不会被调用。
AsyncTask在具体的使用过程中有一些条件限制,如下
(1)AsyncTask的类必须在主线程中加载,这就意味着第一次访问AsyncTask必须发生在主线程,当然在这个过程中Android4.1以上版本已经被系统自动完成,在Android5.0的源码中,可以查看ActivityThread的main方法,它会调用AsyncTask的init方法,这就满足了AsyncTask的类必须在主线程中进行加载这个条件了。
(2)AsyncTask的对象必须在主线程中创建。
(3)execute方法必须在UI线程调用。
(4)不要在程序中直接调用onPreExecute()、onPostExecute、doInBackground和onProgressUpdate方法。
(5)一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常。
(6)在Android1.6之前,AsyncTask是串行执行任务的,Android1.6的时候AsyncTask开始采用线程池里处理并行任务。但从Android3.0开始,为了避免AsyncTask所带来的并发错误,又采用一个线程来串行执行任务。但是在Android3.0以后,仍然可以通过AsyncTask的executeOnExecutor方法来并行执行任务。
AsyncTask的工作原理
HandlerThread
继承了Thread,它是一种可以使用Handler的Thread,它的实现也很简单,就是在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环,这样在实际的使用中就允许在HandlerThread中创建Handler了。
普通Thread主要用于在run方法中执行一个耗时任务,而HandlerThread在内部创建了消息队列,外界需要通过Handler的消息方式来通知HandlerThread执行一个具体的任务。HandlerThread是一个很有用的类,它在Android中的一个具体的使用场景是IntentService,由于HandlerThread的run方法是一个无限循环,因此当明确不需要再使用HandlerThread时,可以通过它的quit或者quitSafely方法来终止线程 的执行,这是一个良好的编程习惯。
IntentService
IntentService是一个特殊的Service,它继承了Service并且它是一个抽象类,因此必须创建它的子类才能使用IntentService,它可以执行后台耗时任务,当任务执行后它会自动停止,同时由于IntentService是服务的原因,它的优先级比单纯的线程要高很多,所以IntentService比较适合执行一些高优先级的后台任务,因为它优先级高,不容易被系统杀死。IntentService封装了HandlerThread和Handler.
IntentService被第一次启动时,它的onCreate方法会被调,onCreate方法会创建一个HandlerThread然后使用它的Looper来构造一个Handler对象mServiceHandler,这样通过mServiceHandler发送的消息最终都会在HandlerThread中执行。IntentService可以用于执行后台任务,每次启动IntentService,它的onStartCommand方法就会调用一次,IntentService在onStartCommand中处理每个后台任务的Intent。
IntentService会通过stopSelf(int startId)方法来尝试停止服务,而这个时候可能还有其他消息未处理,stopSlef(int startId)则会等待所有的消息都处理完毕后才终止服务。stopSlef(int startId)在尝试停止服务之前会判断最近启动服务的次数是否和sartId相等,如果相等就立刻停止服务,不相等则不停止服务,这个策略可以从AMS的stopServiceToken方法的实现中找到依据。
IntentService的onHandleIntent方法是一个抽象方法,他需要我们在子类中实现,它的作用是从Intent参数中区分具体的任务并执行这些任务。如果目前只存在一个后台任务,那么onHandleIntent方法执行完这个任务后,stopSelf(int startId)就会直接停止服务,如果目前存在多个后台任务,那么当onHandleIntent方法执行完最后一个任务时,stopSelf(int startId)才会直接停止服务,由于每执行一个后台任务就必须启动一次IntentService,而IntentService内部则通过消息的方式向HandlerThread请求执行任务,Handler中的Looper是顺序处理消息的,这就意味着IntentService也是顺序执行后台任务的,当有多个后台任务同时存在时,这些后台任务会按照外界发起的顺序排队执行。