这是一篇
有关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的声明:
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方法
在execute方法中,会检测AsyncTask的当前状态,如果当前为RUNNING或FINISHED状态,系统会抛出异常,当为空闲状态,则置为RUNNING状态。
置为RUNNING状态后会调用onPreExecute方法,同时将参数Params传递给mWorker的mParams。之后调用exec.execute,并传入mFuture,其中exec就是传进来的sDefaultExecutor。
- sDefaultExecutor
sDefaultExecutor是一个串行的线程池,一个进程中的所有的AsyncTask全部在这个串行线程池中排队执行,在executeOnExecute方法中,onPreExecute方法最先执行。
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
即corePoolSize的CPU数加一,maxumumPoolSize的CPU数二倍加一,存活1s,任务缓存队列为LinkedBlockingQueue。
- postResult方法
首先用getHandler方法获取AsyncTask获取内部包含的sHandler,然后发送MESSAGE_POST_RESULT消息。
- sHandler
sHandler是一个静态的Handler对象,为了sHandler能够执行环境从后台切换到主线程,则应使用主线程的Looper,在主线程中创建sHandler。sHandler收到MESSAGE_POST_RESULT后,会调用finish方法。源码如下:
- finish
调用isCancelled()判断任务是否取消,如果取消则调用onCancelled(result),
否则调用onPostExecute(result)。
同时将mStatus设为FINISHED,表示对象执行完毕。
2.2 HandlerThread
2.2.1 简介及实现:
HandlerThread继承了Thread,能够使用Handler,实现简单,节省系统资源开销。实现如下:
2.2.2 源码及分析:
它创建了一个消息队列,外界需要通过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方法。具体如下:
首先实例化新线程,然后获得工作线程的Looper,并且构造一个Handler对象mServiceHandler,
通过mServiceHandler发送的消息都会在HandlerThread中执行。
- onStartCommand
每次启动IntentService都会调用onStartCommand方法,用于处理每个后台任务的Intent。
可以看出在onStartCommand中会调用onStart方法,在onStart方法中,获得ServiceHandler消息的引用,后将消息包装到msg中,之后发送消息。这个消息会在HandlerThread中被处理。
- ServiceHandler方法
IntentService的onHandleIntent方法是一个抽象方法,需要实现,作用是从Intent中区分具体任务并执行,调用执行完任务后,stopSelf会直接停止服务,当存在多个任务时,stopSelf则会在最后一个任务执行完毕后停止服务。
▼
3 线程池
在实际使用中,线程池往往更加方便,和线程相比,线程池性能开销小,可避免因为大量线程抢占资源而堵塞,也更容易管理。
java中线程池的接口是ExecutorService,
默认实现是ThreadPoolExecutor。
3.1 ThreadPoolExecutor:
ThreadPoolExecutor是线程池的实现,构造方法如下
- corePoolSize
核心线程数,核心线程会在线程池中一直存活。若将allowCoreThreadTimeOut设置为true,那么核心线程也将有timepout,会在超时后被杀死。
- maximumPoolSize
最大线程数。
- keepAliveTime
非核心线程限制的超时时长,超过设置时间后非核心线程会被收回,allowCoreThreadTimeOut设置为true后核心线程也会被收回。
- unit
时间的参数单位
- workQueue
任务队列,通过线程池的execute方法提交的Runnable对象储存在其中
- threadFactory
线程工厂,为线程池创建线程
ThreadPoolExecutor在执行线程任务需满足一定的规则
- 首先使用核心线程,在核心线程不够时才启用新线程
- 当任务书大于核心线程数量,那么会被插入等待队列中
- 如果不能插入等待队列,且线程数量未达到最大线程数,则会开启新线程
- 若无法插入等待队列且无法创建新线程,则请求会被拒绝
3.2 线程池的分类:
线程池分为四种不同的功能,分别是:
FixedThreadPool;
CachedThreadPool;
ScheduledThreadPool;
SingkeThreadExecutor。
- FixedThreadPool
通过execute的newFixedThreadPool方法创建,固定大小的线程池,每次提交任务都会创建新的线程,直到池中线程数目达到最大。如果有一个线程异常结束后,会产生一个新的线程补充进去。能够更快地相应外界请求。
具体实现如下:
- CachedThreadPool
一个可以根据需要创建线程的线程池,对于很多短期的异步任务,将有效提高性能,调用execute()将重用已构造的线程,如果没有线程可用,将创造一个新的线程并加入线程池,并移除超过60s未使用的线程。适合执行耗时少的任务。
具体实现如下:
- ScheduledThreadPool
核心线程有限,非核心线程数无限,创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。
具体实现如下:
- SingkeThreadExecutor
创建单线程池,即线程池中只有一个线程,所有的任务是串行执行,如果池中线程因异常结束,会有一个新的线程来代替。以此保证了任务按提交顺序执行。
具体实现如下: