作为开发者,我们都知道在开发过程中遇到耗时操作那是不可避免的,例如网络请求、文件读写、数据库操作等等。Android是单线程模型,这意味着Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。但是Android在UI操作上也做了时间限制,  Activity  ——> 5s 、BroadcastReceiver / ContentProvider  ——> 10s 、 Service  ——> 20s   ,超过这个时间就会报ANR。所以为了避免ANR,我们会开辟新的线程去做这些操作。对于开辟新线程,如果你还是通过 new Thread 的方式去做,那我觉得应该改变了。先不说这样的方式对内存、资源的消耗有多大,有些机型是对一个应用开辟的线程数量是有限制的,超过了就 over了。所以针对这些情况去了解一下Android中的多线程操作还是非常重要的。
 
Android提供了四种常用的操作多线程的方式,分别是:
  1. Handler+Thread
  2. AsyncTask
  3. ThreadPoolExecutor
  4. IntentService
 

1. Handler+Thread

  Android主线程包含一个消息队列(MessageQueue),该消息队列里面可以存入一系列的Message或Runnable对象。通过一个Handler你可以往这个消息队列发送Message或者Runnable对象,通过Looper负责管理线程的消息队列和消息循环,然后处理这些对象。每次你新创建一个Handle对象,它会绑定于创建它的线程(也就是UI线程)以及该线程的消息队列,从这时起,这个handler就会开始把Message或Runnable对象传递到消息队列中,并在它们出队列的时候执行它们,回调 Handler的handlerMessage() 方法。所以Handler流程中,关键的角色:MessageQueue 、 Looper 、Message/Runnable .

优缺点:

  1. Handler用法简单明了,可以将多个异步任务更新UI的代码放在一起,清晰明了
2. 处理单个异步任务代码略显多

适用范围:

  1. 多个异步任务的更新UI

2. AsyncTask

  AsyncTask通过一个阻塞队列BlockingQuery<Runnable>存储待执行的任务,利用静态线程池THREAD_POOL_EXECUTOR提供一定数量的线程,默认128个。在Android 3.0以前,默认采取的是并行任务执行器,3.0以后改成了默认采用串行任务执行器,通过静态串行任务执行器SERIAL_EXECUTOR控制任务串行执行,循环取出任务交给THREAD_POOL_EXECUTOR中的线程执行,执行完一个,再执行下一个。看一下示例代码:
class DownloadTask extends AsyncTask<Integer, Integer, String> { 
     // AsyncTask<Params, Progress, Result> //后面尖括号内分别是参数
@Override
protected void onPreExecute() { //第一个执行方法
super.onPreExecute();
} @Override
protected String doInBackground(Integer... params) {
      //第二个执行方法,onPreExecute()执行完后执行return "执行完毕";
} @Override
protected void onProgressUpdate(Integer... progress) {
       //显示进度super.onProgressUpdate(progress);
} @Override
protected void onPostExecute(String result) {
       //doInBackground返回时触发,换句话说,就是doInBackground执行完后触发super.onPostExecute(result);
}
}

通过示例代码,我们知道AsyncTask重要的几个方法: onPreExecute()  、doInBackground(Integer... params) 、onProgressUpdate(Integer... progress)、onPostExecute(String result) 。这四个方法中onProgressUpdate 默认是不会触发的,需要通过对象调用publishProgress() 方法才被调用。

优缺点:

  1. 处理单个异步任务简单,可以获取到异步任务的进度

  2. 可以通过cancel方法取消还没执行完的AsyncTask

  3. 处理多个异步任务代码显得较多

适用范围:

  1. 单个异步任务的处理

3. ThreadPoolExecutor

  ThreadPoolExecutor提供了一组线程池,可以管理多个线程并行执行。这样一方面减少了每个并行任务独自建立线程的开销,另一方面可以管理多个并发线程的公共资源,从而提高了多线程的效率。所以ThreadPoolExecutor比较适合一组任务的执行。但是我们这里强制不允许使用Executor去创建线程池,而是通过ThreadPoolExcutor的方式,规避资源耗尽的风险。

说明:

1) FixedThreadPool 和 SingleThreadPool : 允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM;
2) CachedThreadPool 和 ScheduledThreadPool : 允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

所以正确方式:(更详细的开发规范,点击这里

int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
int KEEP_ALIVE_TIME = 1;
TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>();
ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES, NUMBER_OF_CORES*2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, taskQueue, new BackgroundThreadFactory(), new DefaultRejectedExecutionHandler());

适用范围

1. 批处理任务

4. IntentService

IntentService继承自Service,是一个经过包装的轻量级的Service,用来接收并处理通过Intent传递的异步请求。客户端通过调用startService(Intent)启动一个IntentService,利用一个work线程依次处理顺序过来的请求,处理完成后自动结束Service。避免在BroadcastReceiver#onReceive()中执行耗时操作,如果有耗时操作,应该创建IntentService完成,而不应该在BroadcastReceiver内创建子线程去做。

特点

  1. 一个可以处理异步任务的简单Service

参考:

https://www.jianshu.com/p/2b634a7c49ec

https://www.cnblogs.com/chendu123/p/6081301.html

Android多线程模型的更多相关文章

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

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

  2. android 多线程概述

    android多线程,一直是一个麻烦的事情,要掌握它的本质,我们需要搞清楚一个问题,linux多线程的本质. 我们这篇文章,来讨论以下的议程: 了解linux的历程,了解android的异步任务机制, ...

  3. 由UI刷新谈到线程安全和Android单线程模型

    1.为什么说invalidate()不能直接在线程中调用? Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在非UI主线程中调用,因为他是违背了单线程模型:A ...

  4. android 多线程

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

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

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

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

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

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

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

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

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

  9. 无废话Android之smartimageview使用、android多线程下载、显式意图激活另外一个activity,检查网络是否可用定位到网络的位置、隐式意图激活另外一个activity、隐式意图的配置,自定义隐式意图、在不同activity之间数据传递(5)

    1.smartimageview使用 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android&q ...

随机推荐

  1. Java日志 #01# 入门

    很多人在学习完一个东西之后就会忘掉自己作为初学者时的体验.. 例如刚接触git的时候自己也是一头雾水,然后别人问起来,老是会说:xxxx#!@#,就是这么回事儿,有什么不好懂的. 其实从不懂到懂,再到 ...

  2. Error: could not open `C:\Java\jre7\lib\amd64\jvm.cfg'

    在运行cmd黑窗口时候出现:Error: could not open `C:\Java\jre7\lib\amd64\jvm.cfg'这样的错误的时候,本人没有删除任何文件,只是重新配置了一下jdk ...

  3. LSTM算法原理理解

    神经网络 模拟人类大脑神经网络结构,每个神经元和其他的神经元相互连接,当它兴奋的时候会向相连的神经元发送化学物质,从而改变神经元的电位,当神经元的电位超过阈值,它会被激活,向其他神经元发送化学物质.其 ...

  4. 斯坦福机器学习ex1.1(python)

    使用的工具:NumPy和Matplotlib NumPy是全书最基础的Python编程库.除了提供一些高级的数学运算机制以外,还具备非常高效的向量和矩阵运算功能.这些对于机器学习的计算任务是尤为重要的 ...

  5. JavaScript 条件判断算法综合实战

    在赌场21点游戏中,玩家可以通过计算牌桌上已经发放的卡牌的高低值来让自己在游戏中保持优势,这就叫21点算法. 根据下面的表格,每张卡牌都分配了一个值.如果卡牌的值大于0,那么玩家应该追加赌注.反之,追 ...

  6. rocketmq消息重复推送的问题

    最近,在公司的测试环境,遇到个问题,每次重启应用重启后,原来消费过的消息又被重复推送了一遍,消费者和生产者代码如下: package com.tf56.queue.client; import jav ...

  7. Android之电话拨号和短信

    拨打电话号码,需要添加权限设置,在安装的应用程序信息中可以看到此权限信息Intent doSth=new Intent();//意图:你想做什么呢?doSth.setAction(Intent.ACT ...

  8. opencv学习之路(17)、边缘检测

    一.概述 二.canny边缘检测 #include "opencv2/opencv.hpp" using namespace cv; void main() { //Canny边缘 ...

  9. DataSnap下的分包获取

    DataSnap下通过TQuery—TDataSetProvider—TClientDataSet获取数据,如果是主从数据,则每条主表记录都会触发从表数据的获取. 这种获取和组织数据的方式有一个问题: ...

  10. 终于记住回车和换行cr lf的来由和含义了 -参考: http://www.cnblogs.com/me115/archive/2011/04/27/2030762.html

    回车: carriage return, 是将光标在同一行中, 回到当前行的 行首. 回来的本意就是 返回.. 所以 是同一行的行首. CR 换行: line feed: feed: 饲养(动物); ...