Android几种多线程机制和适用类型:
- AsyncTask: 为 UI 线程与工作线程之间进行快速的切换提供一种简单便捷的机制。适用于当下立即需要启动,但是异步执行的生命周期短暂的使用场景。
- HandlerThread: 为某些回调方法或者等待某些任务的执行设置一个专属的线程,并提供线程任务的调度机制。
- ThreadPool: 把任务分解成不同的单元,分发到各个不同的线程上,进行同时并发处理。
- IntentService: 适合于执行由 UI 触发的后台 Service 任务,并可以把后台任务执行的情况通过一定的机制反馈给 UI。
1.AsyncTask机制
AsyncTask一次性的短暂操作,这里的短暂是指生命周期短暂,不用考虑内存泄露和生命周期的问题。但是这样操作也应该在对面UI线程也是繁重的,所有要使用异步线程处理。
AsyncTask处理任务可以是线性的也可以是并发的,我们可以使用 AsyncTask.executeOnExecutor()强制指定AsyncTask 使用线程池并发调度任务。
如何取消AsyncTask 的执行,AsyncTask提供了cancel()的方法线程本身并不具备中止正在执行的代码的能力,为了能够让一个线程更早的被销毁,我们需要在 doInBackground()的代码中不断的添加程序是否被中止的判断逻辑(isCancel());
使用 AsyncTask 很容易导致内存泄漏,一旦把 AsyncTask 写成 Activity 的内部类的形式就很容易因为 AsyncTask 生命周期的不确定而导致 Activity 发生泄漏。
使用场景:用户切换到某个界面,触发了界面上的图片的加载操作,因为图片的加载相对来说耗时比较长,我们需要在子线程中处理图片的加载,当图片在子线程中处理完成之后,再把处理好的图片返回给主线程,交给 UI 更新到画面上。
2.HandlerThread机制
HandlerThread 比较合适处理那些在工作线程执行,需要花费时间偏长的任务。我们只需要把任务发送给 HandlerThread,然后就只需要等待任务执行结束的时候通知返回到主线程就好了。
Looper: 能够确保线程持续存活并且可以不断的从任务队列中获取任务并进行执行(其实就是个死循环不断获取任务)。
Handler: 能够帮助实现队列任务的管理,不仅仅能够把任务插入到队列的头部,尾部,还可以按照一定的时间延迟来确保任务从队列中能够来得及被取消掉(发送任务和处理任务回调)。
MessageQueue: 使用 Intent,Message,Runnable 作为任务的载体在不同的线程之间进行传递。(存放任务即Message和意图即异步函数的载体)
另外很重要的一点是,一旦我们使用了 HandlerThread,需要特别注意给 HandlerThread 设置不同的线程优先级,CPU 会根据设置的不同线程优先级对所有的线程进行调度优化。
3.ThreadPool机制
线程池适合用在把任务进行分解,并发进行执行的场景。
为了能够实现的线程池模型,系统为我们提供ThreadPoolExecutor 帮助类来简化实现,剩下需要做的就只是对任务进行分解就好了。(由于未使用过线程池,不附加个人理解。)
4.IntentService机制
IntentService 就不仅仅具备了异步线程的特性,还同时保留了 Service 不受主页面生命周期影响的特点。
使用场景:我们可以在 IntentService 里面通过设置闹钟间隔性的触发异步任务,例如刷新数据,更新缓存的图片或者是分析用户操作行为等等,当然处理这些任务需要小心谨慎。
使用 IntentService 需要特别留意以下几点:
- IntentService的任务是线性处理的。
- 通常使用到 IntentService 的时候,我们会结合使用 BroadcastReceiver 把工作线程的任务执行结果返回给主 UI 线程。最好使用LocalBroadcastManager 来发送只在程序内部传递的广播。也可以使用 runOnUiThread() 快速回调到主 UI 线程。
- 含正在运行的 IntentService 的程序相比起纯粹的后台程序更不容易被系统杀死,该程序的优先级是介于前台程序与纯后台程序之间的。
Loader(加载器)
Loader 的出现就是为了确保工作线程能够和 Activity 的生命周期保持一致和自动的加载动态数据。
Thread Priority(线程优先级)
Android 系统会根据当前运行的可见的程序和不可见的后台程序对线程进行归类,划分为 forground 的那部分线程会大致占用掉 CPU 的90%左右的时间片,background 的那部分线程就总共只能分享到5%-10%左右的时间片。之所以设计成这样是因为 forground 的程序本身的优先级就更高,理应得到更多的执行时间。
在 Android 系统里面,我们可以通过android.os.Process.setThreadPriority(int) 设置线程的优先级,参数范围从-20到19,数值越小优先级越高。
主线程默认为0。