这是一篇

有关Android线程及开发应用的分享

在Android中,线程分为主线程和子线程。主界面用于与用户交互,进行UI的相关操作,而子线程则负责耗时操作。如果在主线程中进行耗时操作,就会使程序无法及时的响应。因此,耗时操作必须放在子线程中进行。

为了方便你快速清晰地了解线程与安卓开发,


 1   主线程和子线程

主线程是指进程所有用的线程,在Android中即指进行UI与用户交互的线程就是主线程。因此在Android开发中,需要尽可能的把耗时操作,网络请求访问操作,数据库读取操作等放在子线程,以避免主线程长期处于占用状态以降低用户体验。系统要求网络访问必须在子线程中进行,否则会抛出NetworkOnMainThreadException异常。

 2   线程形态

Android中的线程形态有传统的Thread,AsyncTask,HandlerThread和IntentService。

 2.1   AsyncTask 

AsyncTask封装了Thread和Handler,必须在主线程进行调用,它可以在子线程中执行任务,然后将执行的结果传递给主线程并更新UI。但AsyncTask并不适合执行特别耗时的任务。

 2.1.1   参数:

AsyncTask是一个泛型类,提供了三个泛型参数:Params,Progress和Result。

  • Params表示参数的类型
  • Progress表示后台任务的执行进度的类型
  • Result表示后台任务返回结果的类型

AsyncTask的声明:

android tid 主线程 子线程 安卓子线程_线程池

 2.1.2   方法:

AsyncTask提供了一些核心方法:

  • onPreExecute()在主线程中调用用来进行异步任务的准备操作。
  • doInBackground(Params……params)在执行完onPreExecute()后进行子线程任务时自动调用,Params表示异步任务的输入参数。在方法中可以通过publishProgress更新任务的完成进度,同时在结束调用后会返回结果给onPostExecute()方法。
  • onProgressUpdate(Params……params)在主线程中用于显示任务进行的进度,在publishProgress()方法中被调用。
  • onProgressExecute(Result result)在主线程中用户获取任务结束后回返的结果,即doInBackground的返回值。
  • onCancelled()在主线程中执行,当异步任务被取消后不会执行onProgressExecute方法而会执行onCancelled方法。

 2.1.3   调用:

AsyncTask的使用步骤如下:1.创建AsyncTask子类,具体实现其核心方法2.创建子类的实例对象3.调用execute执行异步线程任务

 2.1.4   AsyncTask的限制:

1.必须在主线程加载,即第一次访问AsyncTask必须在主线程,对象必须在主线程创建2.execute必须在UI线程中调用3.不能在程序中直接调用onPreExecute(),onPostExecute(),

doInBackground和onProgressUpdate方法。

4.一个AsyncTask方法只能使用一次

 2.1.5   AsyncTask的工作原理:

  • execute方法

android tid 主线程 子线程 安卓子线程_线程池_02

在execute方法中,会检测AsyncTask的当前状态,如果当前为RUNNING或FINISHED状态,系统会抛出异常,当为空闲状态,则置为RUNNING状态。

置为RUNNING状态后会调用onPreExecute方法,同时将参数Params传递给mWorker的mParams。之后调用exec.execute,并传入mFuture,其中exec就是传进来的sDefaultExecutor。

  • sDefaultExecutor

sDefaultExecutor是一个串行的线程池,一个进程中的所有的AsyncTask全部在这个串行线程池中排队执行,在executeOnExecute方法中,onPreExecute方法最先执行。

android tid 主线程 子线程 安卓子线程_主线程_03

mTasks代表了SerialExecutor这个串行线程池的任务缓存队列,之后用offer向任务缓存队列中添加了一个任务,调用了r的run方法,r就是传入的mFuture,而mFuture的run方法内部会调用MWorker的call方法,接着调用doInBackground方法,开始执行后台任务。执行结束后会调用scheduleNext方法,执行下一个任务。

另外mActive表示了AsyncTask的对象,如果MActive为null,则同样会执行scheduleNext方法。在scheduleNext方法中,若缓存队列不为空,则从队列中取出任务执行。

综上,SerialExecutor的工作是将任务加入缓存队列中,而执行任务的是THREAD_POOL_EXECUTOR。

  • THREAD_POOL_EXECUTOR

android tid 主线程 子线程 安卓子线程_线程池_04

即corePoolSize的CPU数加一,maxumumPoolSize的CPU数二倍加一,存活1s,任务缓存队列为LinkedBlockingQueue。

  • postResult方法

android tid 主线程 子线程 安卓子线程_主线程_05

首先用getHandler方法获取AsyncTask获取内部包含的sHandler,然后发送MESSAGE_POST_RESULT消息。

  • sHandler

android tid 主线程 子线程 安卓子线程_主线程_06

sHandler是一个静态的Handler对象,为了sHandler能够执行环境从后台切换到主线程,则应使用主线程的Looper,在主线程中创建sHandler。sHandler收到MESSAGE_POST_RESULT后,会调用finish方法。源码如下:

  • finish

android tid 主线程 子线程 安卓子线程_主线程_07

调用isCancelled()判断任务是否取消,如果取消则调用onCancelled(result),

否则调用onPostExecute(result)。

同时将mStatus设为FINISHED,表示对象执行完毕。

 2.2   HandlerThread 

 2.2.1   简介及实现:

HandlerThread继承了Thread,能够使用Handler,实现简单,节省系统资源开销。实现如下:

android tid 主线程 子线程 安卓子线程_android tid 主线程 子线程_08

 2.2.2   源码及分析:

android tid 主线程 子线程 安卓子线程_android tid 主线程 子线程_09

它创建了一个消息队列,外界需要通过Handler的消息通知它执行一个任务,由于HandlerThread的run方法是一个无限循环方法,所以可以通过quit方法终止线程的执行。

 2.3   IntentSerVice:

IntentService是特殊的Service,继承了Service,因为IntentService是一个抽象类,所以必须创建IntentService的子类才能使用。

同时,IntentService是服务,所以在执行时,优先级较高。

IntentService封装了HandlerThread和Handler。

 2.3.1   使用:

1.定义IntentService子类,传入线程名称,复写onHandlerIntent()方法。2.在Manifest.xml中注册3.在Activity中开启服务

 2.3.2   源码分析:

  • onCreate在IntentThread被第一次启动时,会调用相应的onCreate方法。具体如下:

android tid 主线程 子线程 安卓子线程_主线程_10

首先实例化新线程,然后获得工作线程的Looper,并且构造一个Handler对象mServiceHandler,

通过mServiceHandler发送的消息都会在HandlerThread中执行。

  • onStartCommand

每次启动IntentService都会调用onStartCommand方法,用于处理每个后台任务的Intent。

android tid 主线程 子线程 安卓子线程_主线程_11

可以看出在onStartCommand中会调用onStart方法,在onStart方法中,获得ServiceHandler消息的引用,后将消息包装到msg中,之后发送消息。这个消息会在HandlerThread中被处理。

  • ServiceHandler方法

android tid 主线程 子线程 安卓子线程_线程池_12

IntentService的onHandleIntent方法是一个抽象方法,需要实现,作用是从Intent中区分具体任务并执行,调用执行完任务后,stopSelf会直接停止服务,当存在多个任务时,stopSelf则会在最后一个任务执行完毕后停止服务。

 3  线程池

在实际使用中,线程池往往更加方便,和线程相比,线程池性能开销小,可避免因为大量线程抢占资源而堵塞,也更容易管理。

java中线程池的接口是ExecutorService,

默认实现是ThreadPoolExecutor。

 3.1 ThreadPoolExecutor: 

ThreadPoolExecutor是线程池的实现,构造方法如下

android tid 主线程 子线程 安卓子线程_主线程_13

  • corePoolSize

核心线程数,核心线程会在线程池中一直存活。若将allowCoreThreadTimeOut设置为true,那么核心线程也将有timepout,会在超时后被杀死。

  • maximumPoolSize

最大线程数。

  • keepAliveTime

非核心线程限制的超时时长,超过设置时间后非核心线程会被收回,allowCoreThreadTimeOut设置为true后核心线程也会被收回。

  • unit

     时间的参数单位

  • workQueue

任务队列,通过线程池的execute方法提交的Runnable对象储存在其中

  • threadFactory

线程工厂,为线程池创建线程

ThreadPoolExecutor在执行线程任务需满足一定的规则

  • 首先使用核心线程,在核心线程不够时才启用新线程
  • 当任务书大于核心线程数量,那么会被插入等待队列中
  • 如果不能插入等待队列,且线程数量未达到最大线程数,则会开启新线程
  • 若无法插入等待队列且无法创建新线程,则请求会被拒绝

 3.2  线程池的分类: 

线程池分为四种不同的功能,分别是:

FixedThreadPool;

CachedThreadPool;

ScheduledThreadPool;

SingkeThreadExecutor。

  • FixedThreadPool

通过execute的newFixedThreadPool方法创建,固定大小的线程池,每次提交任务都会创建新的线程,直到池中线程数目达到最大。如果有一个线程异常结束后,会产生一个新的线程补充进去。能够更快地相应外界请求。

具体实现如下:

android tid 主线程 子线程 安卓子线程_线程池_14

  • CachedThreadPool

一个可以根据需要创建线程的线程池,对于很多短期的异步任务,将有效提高性能,调用execute()将重用已构造的线程,如果没有线程可用,将创造一个新的线程并加入线程池,并移除超过60s未使用的线程。适合执行耗时少的任务。

 具体实现如下:

android tid 主线程 子线程 安卓子线程_android tid 主线程 子线程_15

  • ScheduledThreadPool

核心线程有限,非核心线程数无限,创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。

 具体实现如下:

android tid 主线程 子线程 安卓子线程_android tid 主线程 子线程_16

  • SingkeThreadExecutor

创建单线程池,即线程池中只有一个线程,所有的任务是串行执行,如果池中线程因异常结束,会有一个新的线程来代替。以此保证了任务按提交顺序执行。

 具体实现如下:

android tid 主线程 子线程 安卓子线程_线程池_17