为主线程减轻负的多线程方案有哪些呢?这些方案分别适合在什么场景下使用?

Android系统为我们提供了若干组工具类来帮助解决这个问题。

  • AsyncTask: 为UI线程与工作线程之间进行快速的切换提供一种简单便捷的机制。适用于当下立即需要启动,但是异步执行的生命周期短暂的使用场景。
  • HandlerThread: 为某些回调方法或者等待某些任务的执行设置一个专属的线程,并提供线程任务的调度机制。
  • ThreadPool: 把任务分解成不同的单元,分发到各个不同的线程上,进行同时并发处理。
  • IntentService: 适合于执行由UI触发的后台Service任务,并可以把后台任务执行的情况通过一定的机制反馈给UI。

1. HandlerThread

Android系统为我们提供了Looper,Handler,MessageQueue来帮助实现上面的线程任务模型:

Looper: 能够确保线程持续存活并且可以不断的从任务队列中获取任务并进行执行。

Handler: 能够帮助实现队列任务的管理,不仅仅能够把任务插入到队列的头部,尾部,还可以按照一定的时间延迟来确保任务从队列中能够来得及被取消掉。

MessageQueue: 使用Intent,Message,Runnable作为任务的载体在不同的线程之间进行传递。

把上面三个组件打包到一起进行协作,这就是HandlerThread

程序被启动,系统会帮忙创建进程以及相应的主线程,而这个主线程其实就是一个HandlerThread。这个主线程会需要处理系统事件,输入事件,系统回调的任务,UI绘制等等任务,为了避免主线程任务过重,我们就会需要不断的开启新的工作线程来处理那些子任务。

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

特点:
(1)  HandlerThread将loop转到子线程中处理,分担MainLooper的工作量,降低了主线程的压力,使主界面更流畅。
(2)  HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程。
(3)  处理任务是串行执行,按消息发送顺序进行处理。由于每一个任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那       么就会导致后续的任务都会被延迟处理。
(4)  对于网络IO操作,HandlerThread并不适合,因为它只有一个线程,还得排队一个一个等着。

 2. AsyncTask

使用AsyncTask需要注意的问题有哪些呢?请关注以下几点:

(1)  默认情况下,所有的AsyncTask任务都是被线性调度执行的,他们处在同一个任务队列当中,按顺序逐个执行。

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

(2)  其次,如何才能够真正的取消一个AsyncTask的执行呢?

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

一旦任务被成功中止,AsyncTask就不会继续调用onPostExecute(),而是通过调用onCancelled()的回调方法反馈任务执行取消的结果。我们可以根据任务回调到哪个方法(是onPostExecute还是onCancelled)来决定是对UI进行正常的更新还是把对应的任务所占用的内存进行销毁等。

(3)  最后,使用AsyncTask很容易导致内存泄漏,在Activity内部定义的一个AsyncTask,它属于一个内部类,该类本身和外面的Activity是有引用关系的,如果Activity要销毁的时候,AsyncTask还仍然在运行,这会导致Activity没有办法完全释放,从而引发内存泄漏。

3. ThreadPoolExecutor

Runtime.getRuntime().availableProcesser()方法并不可靠,他返回的值并不是真实的CPU核心数,因为CPU会在某些情况下选择对部分核心进行睡眠处理,在这种情况下,返回的数量就只能是激活的CPU核心数。

使用线程池ThreadPool的好处:

(1)  重用已经创建好的线程,避免频繁创建进而导致的频繁GC
(2)  控制线程并发数,合理使用系统资源,提高应用性能
(3)  可以有效的控制线程的执行,比如定时执行,取消执行等

4. IntentService

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

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

5. 线程优先级

Android系统会根据当前运行的可见的程序和不可见的后台程序对线程进行归类,划分为forground的那部分线程会大致占用掉CPU的90%左右的时间片,background的那部分线程就总共只能分享到5%-10%左右的时间片。默认情况下,新创建的线程的优先级默认和创建它的母线程保持一致。如果主UI线程创建出了几十个工作线程,这些工作线程的优先级就默认和主线程保持一致了,为了不让新创建的工作线程和主线程抢占CPU资源,需要把这些线程的优先级进行降低处理,这样才能给帮组CPU识别主次,提高主线程所能得到的系统资源。

我们可以通过android.os.Process.setThreadPriority(int)设置线程的优先级,参数范围从-20到24,数值越小优先级越高。

Android系统里面的AsyncTask与IntentService已经默认帮助我们设置线程的优先级,

AsyncTask: Process.THREAD_PRIORITY_BACKGROUND

IntentService: Process.THREAD_PRIORITY_DEFAULT

6. 问题

在Activity不停的创建与销毁的过程当中,很有可能因为工作线程持有Activity的View而导致内存泄漏(因为工作线程很可能持有View的强引用,另外工作线程的生命周期还无法保证和Activity的生命周期一致,这样就容易发生内存泄漏了).

Loader的出现就是为了确保工作线程能够和Activity的生命周期保持一致,同时避免出现前面提到的问题。

在Activity或者Fragment中使用Loader可以方便的实现异步加载的框架,Loader有诸多优点。

Android多线程方案的更多相关文章

  1. Android多线程----异步消息处理机制之Handler详解

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

  2. Andoid 更好的Android多线程下载框架

    概述 为什么是更好的Android多线程下载框架呢,原因你懂的,广告法嘛! 本篇我们我们就来聊聊多线程下载框架,先聊聊我们框架的特点: 多线程 多任务 断点续传 支持大文件 可以自定义下载数据库 高度 ...

  3. 更好的Android多线程下载框架

    /** * 作者:Pich * 原文链接:http://me.woblog.cn/ * QQ群:129961195 * Github:https://github.com/lifengsofts */ ...

  4. android 多线程

    本章讲述在android开发中,多线程的应用.多线程能够处理耗时的操作并优化程序的性能.本章主要介绍知识点,AsyncTask,Java线程池,ThreadPoolExecutor线程池类.本章案例只 ...

  5. Android多线程分析之五:使用AsyncTask异步下载图像

    Android多线程分析之五:使用AsyncTask异步下载图像 罗朝辉 (http://www.cnblogs.com/kesalin) CC 许可,转载请注明出处 在本系列文章的第一篇<An ...

  6. Android多线程分析之四:MessageQueue的实现

    Android多线程分析之四:MessageQueue的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处 在前面两篇文章<Androi ...

  7. Android多线程分析之三:Handler,Looper的实现

    Android多线程分析之三:Handler,Looper的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处 在前文<Android多 ...

  8. Android多线程分析之二:Thread的实现

    Android多线程分析之二:Thread的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处   在前文<Android多线程分析之一 ...

  9. Android多线程分析之一:使用Thread异步下载图像

    Android多线程分析之一:使用Thread异步下载图像 罗朝辉 (http://www.cnblogs.com/kesalin) CC 许可,转载请注明出处   打算整理一下对 Android F ...

随机推荐

  1. 阶段1 语言基础+高级_1-3-Java语言高级_04-集合_06 Set集合_1_HashSet集合介绍

    特点:不允许有重复的记录,无序的集合 set不允许重复.接口中没有索引.所以方法和Collection中的方法是一样的,没有带索引的方法 因为Set的方法和Collection都是一样的.所以这里不再 ...

  2. Notepad++的tab设置为四个空格

    参考:https://www.cnblogs.com/jyfootprint/p/9409934.html 1.Python使用缩进来组织代码块,坚持使用4个空格的缩进. 在文本编辑器中,需要设置把T ...

  3. javascript中json字符串对象转化

    li = [1,2,3,4] s = JSON.stringify(li)  ---转化为字符串 JSON.parse(s) --转化为对象

  4. Java反序列化漏洞整理

    Fastjson 反序列化 CVE-- Fastjson 利用版本范围为 Fastjson 及之前的版本 Struts2 S2-, S2-, S2-, S2-, S2-, S2-, S2-, S2-, ...

  5. [13期]mysql-root全手工注入写马实例实战

    回显方便的小工具

  6. debian下使用shell脚本时出现了 declare:not found 解决方法

    问题:出现declare:not found的提示 解决:原来,UBUNTU用的是dash(后来证明这个其实这个不是错误的原因:从#!/bin/bash到#!/bin/dash,依旧无法运行,在这写出 ...

  7. 20190906 On Java8 第十八章 字符串

    第十八章 字符串 +的重载与StringBuilder 用于String的+与+=是Java中仅有的两个重载过的操作符,Java不允许程序员重载任何其他的操作符.编译器自动引入了java.lang.S ...

  8. Java提取文本文档中的所有网址(小案例介绍正则基础知识)

    正则表达式基础以及Java中使用正则查找 定义: 正则表达式是一些用来匹配和处理文本的字符串 正则的基础(先大致了解下) 1. 正则表达式的作用 查找特定的信息(搜索) 替换一些文本(替换) 2. 正 ...

  9. xshell输入字母空格间距变大

    按一下shift+空格(全角/半角转换的快捷键,引起的问题)

  10. Pycharm 修改项目名称后 中括号中出现先前名称怎么解决?

    这时候,你打开工程的路径,会找到一个隐藏文件, .idea  目录删掉他,重新导入Pycharm 就行了. idea记录了一些工程项目信息. 步骤: 1.退出当前工程 2.打开工程路径删除.ideal ...