前言:

android线程通讯机制是android应用开发的基础课程,对于很多初学android的朋友可能还没有完全理解,所以,今天我就做一下知识小结吧。

一、线程安全

可能有java基本的朋友都知道什么叫线程安全。

线程安全:如果你的代码在所在的进程中有多个(两个或两个以上)的线程同时执行,若每次运行的结果和使用单线程模式运行的结果一致,并且变量的值也和预期的一样,这样就叫线程安全。或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。

在android系统中,当一个android应用启动的时候,会开启一个Lliunx进程和主线程,这个主线程我们有称UI线程。UI线程是负责显示,处理UI以及相关事件的线程。

android的UI操作不是线程安全,就是说要UI线程才能处理UI控件,而且当UI线程阻塞超过5秒的时候,系统会报出Force Close的强制关闭的提示。所以,如果要进行比较耗时的IO操作的时候,android系统就会出现假死的状态,甚至出现FC提示,就如下载文件、打开大文件等等。

二、线程间通讯实现

面对以上问题,我们为了让应用在用户面前展现更加流畅和平滑的效果(就是更好的用户体验),我们需要多线程操作,所以,我们要考虑的是,如果让UI线程和子线程交互。

强大的google开发的android系统里面已经提供了很优秀的线程通讯机制。

1、多线程任务开发可以通过以下几个方式实现:

1.1、Handler+Message+Thread

1.2、HandlerThread

1.3、AsyncTask

2、如果子线程的数据想通知到UI线程中,可以一下的实现方法:

2.1、上述的三种方法

2.2、Activity.runOnUIThread(Runnable)

2.3、View.post(Runnable)

2.4、View.postDelayed(Runnable, long)

三、分析

1、Handler+Message+Thread(Runnable)

Handler

1、发送消息到消息队列

2、发送Runnable任务到消息队列

3、从消息队列中接受消息并在当前线程处理消息

4、从消息队列中接受Runnable任务并执行

5、按时间计划(指定时间、延迟时间、每隔时间段)来执行任务或者处理消息

Handler中分发消息的一些方法
        post(Runnable)
        postAtTime(Runnable,long)
        postDelayed(Runnable long)
        sendEmptyMessage(int)
        sendMessage(Message)
        sendMessageAtTime(Message,long)
        sendMessageDelayed(Message,long)
        以上post类方法允许你排列一个Runnable对象到主线程队列中,
        sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新.

Message

封装了一系列的参数的消息实体类

Thread(Runnable)

子线程

消息通知简单例子:

[html] view plain copy
  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3. // TODO Auto-generated method stub
  4. super.onCreate(savedInstanceState);
  5. /* 子线程执行耗时任务 */
  6. new Thread() {
  7. public void run() {
  8. // 执行耗时的任务
  9. try {
  10. Thread.currentThread().sleep(7000);
  11. } catch (Exception err) {
  12. }
  13. // 通过Handler获得Message对象
  14. Message msg = new MyHandler().obtainMessage();
  15. msg.obj = "任务执行完毕";
  16. // 发送到Handler,在UI线程里处理Message
  17. msg.sendToTarget();
  18. }
  19. }.start();
  20. }
  21. /* 重写handleMessage方法,接受并处理Message消息 */
  22. public class MyHandler extends Handler {
  23. public void handleMessage(Message msg) {
  24. // 处理接受到的Message
  25. System.out.println("接受到消息:" + msg.obj + ",并成功处理");
  26. }
  27. }

注意:不能在子线程操作UI线程中的UI控件,因为之前也说过UI线程是线程不安全的,有的人可能操作了UI但是也没有报错。这是因为此时UI资源没有被争夺,没有产生死锁,但是也存在这个问题,所以要编写高质量的代码就要编写健壮性强的代码。所以只能在Handler里面来操作UI。

如果使用post(Runnable)、postAtTime(Runnable,long)、postDelayed(Runnable long)方法,里面的Runnable可以对UI操作,因为最后UI线程接受到Runnable的任务时,是直接执行run()方法,所以本质上在UI线程执行任务。

2、HandlerThread

Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.

1、本身继承Thread,就是一个子线程

2、和Handler+Message+Thread的区别是:在Handler类里面处理Message消息可以是耗时操作,但是不能对UI操作。就是说把handleMessage方法在子线程执行了,而不是在UI线程执行,所以这个工具类应用不广,只是多线程处理接受到的Message对象。

下面是一个简单的例子:

[java] view plain copy
  1. public void onCreate(Bundle savedInstanceState) {
  2. super.onCreate(savedInstanceState);
  3. HandlerThread hThread = new HandlerThread("myThread");
  4. hThread.start();
  5. myhandler myhandler = new myhandler(hThread.getLooper());
  6. Message msg = myhandler.obtainMessage();
  7. msg.sendToTarget();// 把 Message发送到目标对象,目标对象就是生成msg的目标对象。
  8. }
  9. class myhandler extends Handler {
  10. //必须调用Handler(Looper looper)方法
  11. public myhandler(Looper looper) {
  12. super(looper);
  13. }
  14. public void handleMessage(Message msg) {
  15. Log.e("这是新线程", "》》》》》》》》》》》》》》》》》新线程的测试");
  16. }
  17. }

3、AsyncTask

官方的描述(这个我就不翻译了):

AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

为了解决多线程执行任务的问题,Android 1.5提供了一个工具类:AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单。相对来说AsyncTask更轻量级一些,适用于简单的异步处理,不需要借助线程和Handler即可实现。其实说白了,这个工具类只是对线程和Handler的一个封装,具体内部如何实现我将会在提高篇解析。

这个工具类使用很简单主要有以下四个步骤:

1、继承或者实现AsyncTask类(因为是abstract 修饰的抽象类)

2、实现以下方法

(1)onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。 
(2)doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。 
   (3)onProgressUpdate(Progress...),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。 
    (4)onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.

3、在UI线程中实例化定义好的AsyncTask或者其子类

4、调用execute(Params...)开始执行任务

为了正确使用AsyncTask类,必须遵循一下准则:

1、AsyncTask实例必须在UI线程中创建

2、execute方法必须在UI线程中调用

3、不允许手动调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法

4、AsyncTask的execute(Params...)执行方法只能执行一次,就是一个实例只能执行一次,执行多次会出现异常。需要说明AsyncTask不能完全取代线程,在一些逻辑较为复杂或者需要在后台反复执行的逻辑就可能需要线程来实现了。

下面有个简单的例子:

[java] view plain copy
  1. public void onCreate(Bundle savedInstanceState) {
  2. super.onCreate(savedInstanceState);
  3. //实例化AsyncTask
  4. AsyncTask task = new DownloadFilesTask();
  5. //开始执行任务
  6. try {
  7. task.execute(new URL("www.hclab.cn"));
  8. } catch (MalformedURLException e) {
  9. e.printStackTrace();
  10. }
  11. }
  12. /*一个加载URL的异步任务*/
  13. private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
  14. /* 异步执行 */
  15. protected Long doInBackground(URL... urls) {
  16. int count = urls.length;
  17. long totalSize = 0;
  18. for (int i = 0; i < count; i++) {
  19. totalSize += Downloader.downloadFile(urls[i]);
  20. publishProgress((int) ((i / (float) count) * 100));
  21. // Escape early if cancel() is called
  22. if (isCancelled()) break;
  23. }
  24. return totalSize;
  25. }
  26. /* 任务数据更新 */
  27. protected void onProgressUpdate(Integer... progress) {
  28. setProgressPercent(progress[0]);
  29. }
  30. /* 处理最后执行结果 */
  31. protected void onPostExecute(Long result) {
  32. showDialog("Downloaded " + result + " bytes");
  33. }
  34. }

四、总结

这篇博文只是对android多线程处理任务和子线程和UI线程的通讯实现方法做了简单的介绍。所以说,只是适合新手过目,这个也是android应用开发的基本功吧。通常学习某方面的知识的时候不要只局限于表面,聪明的人可能会问,为什么会这样实现的?为什么一定要按照的步骤实现?为什么会有这么多中实现方法,内部会不会是一样的实现原理呢?等等。

对,其实我想告诉初学者的一点学习建议(个人看法,适合自己的学习方法才是最重要):学习android api的时候,首先要学会用,用熟了,再去研究其源码或者内部底层实现原理。如果一下子就告诉你这个类的实现原理,可能会混淆你的总体逻辑思想,会越来越乱,连这个类到底是可以做什么都糊涂了。我个人就是这样,在使用新知识的时候,多问问为什么,多想想方方面面,带着问题,再去源码或者实现原理找答案。当你弄懂了,会感觉很爽,哈哈。这样的层次结构更加清晰。

所以,这就是我分基础篇(使用方法)和提高篇(内部原理解析)的原因了(提高篇之后会出)。

原文地址: http://blog.csdn.net/q376420785/article/details/8882920

线程间通讯机制(基础篇)——Handler、Runnable、HandlerThread、AsyncTask的使用相关推荐

  1. 线程间通讯机制(提高篇)——深入浅出实现原理

    前言: 这一篇博文主要是和大家讲解一下线程间通讯机制的内部实现原理,即Handler.Message.MessageQueue.Looper.HandlerThread.AsyncTask类的实现以及 ...

  2. 【转】JAVA 并发性和多线程 -- 读感 (二 线程间通讯,共享内存的机制)

    原文地址:https://www.cnblogs.com/edenpans/p/6020113.html 参考文章:http://ifeve.com/java-concurrency-thread-d ...

  3. android线程间通信的几种方法_Android线程间通信机制

    讲解Handler机制的博文很多,我也看了很多,但说实话,在我对Handler几乎不怎么了解的情况下,每一篇文章我都没太看懂,看完之后脑子里还是充满了疑问.究其原因,是因为几乎每一篇文章一上来就开始深 ...

  4. Android-Binder进程间通讯机制-多图详解

    本系列: Android-Binder进程间通讯机制-多图详解 一次Binder通信最大可以传输多大的数据?​​​​​​​ 关于Binder (AIDL)的 oneway 机制 概述 最近在学习Bin ...

  5. 一篇文章了解相见恨晚的 Android Binder 进程间通讯机制

    概述 最近在学习Binder机制,在网上查阅了大量的资料,也看了老罗的Binder系列的博客和Innost的深入理解Binder系列的博客,都是从底层开始讲的,全是C代码,虽然之前学过C和C++,然而 ...

  6. Android线程间通信机制

    Android线程间通信机制 当android应用程序运行时,一个主线程被创建(也称作UI线程),此线程主要负责处理UI相关的事件,由于Android采用UI单线程模型,所以只能在主线程中对UI元素进 ...

  7. 多线程编程之三——线程间通讯

    七.线程间通讯 一般而言,应用程序中的一个次要线程总是为主线程执行特定的任务,这样,主线程和次要线程间必定有一个信息传递的渠道,也就是主线程和次要线程间要进行通信.这种线程间的通信不但是难以避免的,而 ...

  8. QT不同线程间signal-slot机制的值传递

    QT不同线程间signal-slot机制的值传递 signal-slot机制是QT核心,也是QT解决线程之间通信的一大亮点.深刻理解其两大特性: (1)loosely coupled sender与r ...

  9. RT-Thread学习笔记六——线程间通讯(信号量的使用)

    目录 1.概念 1.1  二值型信号量 1.2  计数型信号量 2.信号量的创建(API) 2.1动态信号量创建 2.2动态信号量的删除 2.3静态信号量的创建 2.4静态信号量的删除 3.信号量的获 ...

最新文章

  1. GAN网络生成:感知损失(Perceptual Losses)
  2. 年终重磅:解密全球30家搅局者和355家上市路上的科技公司
  3. Nginx 作为web server 的优化要点
  4. jsp定义一个变量在html,jsp中变量及方法的声明与使用说明
  5. nodejs的web开发框架了解一下
  6. Java之品优购课程讲义_day01(8)
  7. 2020-11-5(安卓)
  8. 简历上的“熟练掌握 RPC”,到底是个什么水平?
  9. idea一键加密部署springboot到docker容器
  10. Docker 安装solr 配置IK分词,说明
  11. [asp.net mvc]自定义filter
  12. mysql 权限信息存储库_springboot-security02FromDB 权限管理(用户信息和角色信息保存在数据库)详解...
  13. 逆向动态调试之Ollydbg的使用
  14. java 十六进制颜色代码_RGB颜色与16进制颜色的换算方法
  15. echar图形使用双Y轴(散点+折线)
  16. 苹果在中国失掉 iPad 商标
  17. 苹果手机账号验证失败连接不上服务器,苹果手机让检查Apple ID 电话号码点击后验证失败,连接服务器失败出错...
  18. table表格标签css固定最后一列方案
  19. ubuntu 安装 魔霸_ROG 玩家国度 魔霸2怎么安装系统?
  20. 我的asterisk 接入电信ims之旅【把电信座机提取到手机上,实现手机不插卡也用打电话】

热门文章

  1. spring_整体系统
  2. [云炬创业管理笔记]第一章测试2
  3. jsp可以使用iframe_使用 JavaScript object URLs,可以处理图像、音频和视频
  4. openCV视频处理与图像转换
  5. 小波的秘密10_图像处理应用:图像增强
  6. 戏说 Windows GDI (1)
  7. (转)用ASP.NET向Javascript传递变量 方法1:
  8. go语言中fallthrough与break的使用
  9. bash脚本一条命令直接发送http请求
  10. 【CyberSecurityLearning 59】OS命令注入