Android性能优化之多线程

本文主要介绍了AsyncTask, HandlerThread, IntentService与ThreadPool分别适合的场景以及各自的使用注意事项。

一,线程性能

在程序开发的实践当中,为了让程序表现得更加流畅,我们肯定会需要使用到多线程来提升程序的并发执行性能。但是编写多线程并发的代码一直以来是一个相对棘手的问题。

为主线程减轻负担的多线程方案有哪些?这些方案分别适合在什么场景下使用?Android系统为我们提供了若干组工具来帮助解决这个问题。

1)AsyncTask:为UI线程与工作线程之间进行快速的切换提供一种简单便捷的机制。适用于当下立即需要启动,但是异步执行的生命周期短暂的使用场景。

2)HandlerThread:为某些回调方法或者等待某些 任务的执行设置一个专属的线程,并提供线程任务的调度机制。

3)ThreadPool:把任务分解成不同的单元,分发发各个不同的线程上,进行同时并发处理。

4)IntentService: 适合于执行由UI触发的后台Service任务,并可以把后台任务执行的情况通过一定的机制反馈给UI


二、内存与线程

增加并发的线程数会导致内存消耗的增加,平衡好这两者的关系是非常重要的。我们知道,多线程并发访问同一块内存区域有可能带来很多问题,例如读写的权限争夺问题等。为了解决这些问题,我们会需要引入锁的概念。

另外,在非UI线程中直接持有UI对象的引用也很有可能出现问题。例如Work线程中持有某个UI对象的引用,在Work线程执行完毕之前,UI对象在主线程中被ViewHierarchy中移除了,这个时候UI对象的任何属性都已经不再可用了,另外对这个UI对象的更新操作也没有意义了。

不仅如此,View对象本身对所属的Activity是有引用关系的,如果工作线程继续保有View的引用,这就可能导致Activity无法完全释放。除了直接显式的引用关系可能导致内存泄漏外,我们还需要特别留意隐式引用关系。例如我们通常会看到在Activity里面定义一个AsynncTask,这种类型的AsyncTask与外部的Activity是存在隐式引用关系的,只要Task没有结束,引用关系一直存在,这就容易导致Activity的泄漏。

为了解决上面的问题,我们需要谨记的原则是:不要在任何非UI线程里去持有UI对象的引用。


三、AsyncTask

AsyncTask是一个让人既爱又恨的组件,AsyncTask把主线程里面的准备工作放到onPreExecutr()方法里面进行执行,doInBackground()方法执行在工作线程里,用来处理那些繁重的任务,一旦任务执行完毕,就会调用onPostExecute()方法返回到主线程。

使用AsyncTask需要主要的问题有:

首先,默认情况下,所有的AsyncTask任务都是被线性调度执行的,他们处在同一个任务队列当中,按顺序逐个执行。假设你按照顺序启动20个AsyncTask,一旦其中的某个Asynctask执行时间过长,队列中的其他剩余AsyncTask都处于阻塞状态,必须等到该任务执行完毕之后才能够有机会执行下一个任务。

为了解决上面提到的线性队列等待的问题,我们可以使用AsyncTask.executeOnExecuter()强制指定AsyncTask使用线程池并发调度任务。

其次,如何才能真正的取消一个AsyncTask的执行呢?我们知道AsyncTask有提供cancel()的方法,但是这个方法实际上做了什么事情呢?线程本身并不具备中止正在执行的代码的能力,为了能够让一个线程更早的被销毁,我们需要在doInBackground()的代码中不断地添加程序是否被中止的判断逻辑。

最后,使用asyncTask很容易导致内存泄漏。


四、HandlerTread的优势

如果我们需要的是一个执行在工作 线程,同时又能够处理队列中的复杂任务的功能,而HandlerTread的出现就是为了实现这个功能,它组合了Handler、MessageQueue、Looper实现了一个长时间运行的线程,不断地从队列中获取任务进行执行的功能。如果需要更新UI,只需要调用runOnUiThread()方法把任务回调给主线程就够了。

HandlerThread 比较适合处理那些在工作线程执行,需要花费时间偏长的任务。我们只需要把任务发送给HandlerThread,然后就只需要等待任务执行结束的时候通知返回到主线程就好了。


五、ThreadPools

线程池适合用在把任务进行分解,并发进行执行的场景。通常来说,系统里面针对不同的任务设置一个单独的守护线程用来专门处理这项任务。


六、IntentServie

默认的service是执行在主线程的,可是通常情况下,这很容易影响到程序的绘制性能。除了全面介绍的AsyncTask和 HandlerThread,我们还可以选择使用IntentService来实现异步操作。IntentService继承了普通的service同时又在内部创建了一个HandlerThread,在OnHandlerIntent()的回调里面处理扔到IntentService的任务。所以IntentService不仅具备了异步线程的特性,还同时保留了service不受主页面生命周期影响的特点。

使用IntentService需要特别留意一下几点:

首先,因为IntentService内置的是HandlerThread作为异步线程,所以每一个交给IntentService的任务都将以队列的方式逐个被执行到,一旦队列中某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。

其次,通常使用IntentService的时候,我们会结合使用BroadcastReceiver把工作线程的任务执行结果返回给主线程。使用广播容易引起性能问题,我们可以使用LocalBroadcastManager来发送只在程序内部传递的广播,从而提升广播的性能。

最后,包含正在运行的IntentService的程序相比起纯粹的后台程序更不容易被系统杀死,该程序的优先级是介于前台程序与纯后台程序之间的。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值