冰冻三尺,非一日之寒!代码要写,理论也要理解的嘛。Google工程师对Java中的多线程的通信机制进行了更为巧妙的设计,引入了Handler机制。下面我们重点来说说Handler的使用情景以及核心点。

AndroidUI主线程主要负责初始化屏幕组件以及负责处理用户的按键事件等,如果主线程长时间被阻塞,会导致UI界面停止响应(大约是5s),也就是我们常说的ANR异常(现在Android中)。因此建议将耗时的操作放在子线程中完成,但是有时候子线程可能需要动态的更新UI组件,而UI线程是线程不安全的,因此Google禁止在子线程中更新UI组件,为了解决这个问题,Android提出了一下几种方案:



  1. 使用Handler实现线程通信

  2. Activity.runOnUiThread(Runable);

  3. View.post(Runable);

  4. View.postDelayed(Runable,long)

  5. AsyncTask

    (注意234比较繁琐)

    AsyncTask(异步任务)更轻量,不需要借助线程和Handler即可实现。

这里我们重点只谈第一种方式——Handler。在使用Handler之前,我们先简单的了解几个概念。



1MessageQueue


是一种数据结构,就是一个消息队列,存放消息Message的地方。每一个线程最多只可以拥有一个MessageQueue数据结构。创建一个线程的时候,并不会自动创建其MessageQueue。通常使用一个Looper对象对该线程的MessageQueue进行管理。但在主线程创建Handler是,会创建一个默认的Looper对象,而Looper对象的创建,将自动创建一个MessageQueue。其他非主线程也就是子线程,则不会自动创建Looper,因此在创建Handler之前需要先调用Looper儿的prepare函数。

下面我们简单的看一下Looper源代码:

private Looper() {    mQueue = new MessageQueue();    mRun = true;    mThread = Thread.currentThread();  }
</pre><p></p><p style="color:rgb(0,0,0); font-family:'Times New Roman',serif; font-size:10.5pt; font-style:normal; margin-top:0cm; margin-bottom:0pt"></p><p style="font-weight:normal"><strong></strong></p><p><strong>2、<span style="color:red">Message</span>:</strong></p><p style="font-weight:normal">Handler接受和处理的消息对象,线程之间通过Message来通信,同时也是MessageQueue中的存放的对象。一个MessagQueue中包含多个Message。通常使用Message类里的静态方法obtain()来获取Message实例对象,<span style="font-family:'Times New Roman',serif; font-size:14px">obtain()</span>方法有多个重载版本可供选择;要注意的是Message的设计基于消息池,在获取Message对象时,首先先从<span style="color:red">Message Pool(</span><span style="color:red">消息池)</span>中查看是否有可用的Message实例,存在则直接取出返回这个实例。如果Message Pool中没有可用的Message实例,则才用给定的参数创建一个Message对象。此外,也可以通过Handler对象的obtainMessage()获取一个Message实例。</p><p><strong><span style="color:red">3</span><span style="color:red">、Looper</span>:</strong></p><p>MessageQueue的管理者,它会不断的从MessageQueue中取出Message,并将Message分发到指定的子线程。每一个MessageQueue都绑定了一个Looper,也就是说MessageQueue不能脱离Looper存在,而Looper对象的创建则是通过Looper.<span style="color:red">prepare()</span><span style="color:red">函数</span>来实现的。同时每一个Looper对象 和一个线程关联(每个线程只能拥有一个Looper,一个Looper管理一个MessageQueue,而一个Looper可以被多个线程拥有)。在Handler中是通过调用Looper.myLooper()可以获得当前线程的Looper对象 。创建一个Looper对象时,会同时创建一个MessageQueue对象。除了主线程有默认的Looper,其他线程默认是没有<span style="font-family:'Times New Roman',serif; font-size:14px">Looper</span>对象的,所以,不能接受Message。(主线程中,系统已经初始化了一个Looper对象并关联了主线程,因此程序中直接创建Handler即可)子线程中如需要使用Handler,则必须通过prepare0来创建一个Looper对象并绑定当前子线程,这样该线程就有了自己的Looper对象和MessageQueue数据结构了。</p><p>Looper从MessageQueue中取出Message然后,交由Handler的handleMessage进行处理。处理完成后,调用Message.recycle()将其放入Message Pool中。</p><p style="color:rgb(0,0,0); font-family:'Times New Roman','serif'; font-size:10.5pt; font-style:normal; font-weight:normal; margin-top:0cm; margin-bottom:0pt"><span style="font-family:黑体; font-size:12pt"></span></p><pre name="code" class="java" style="font-size: 14px;"> public static void prepare() {        if (sThreadLocal.get() != null) {//一个线程只能有一个Looper            throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper());//线程关联Looper    }

4、Handler

其作用有两个:发送消息和处理消息。

发送消息:调用Handler的sendMessage发送消息Message,被Handler发送的消息Message必须被送到指定的MessageQueue。

处理消息:当Looper对象看到MessageQueue中含有Message,就将其广播到指定的Handler所在的子线程。该Handler对象收到该消息后,调用相应的handler对象的handleMessage(Message msg)方法对其进行处理。

注意:如果希望Handler正常工作,必须在当前线程中有一个Looper对象,因此如果要在自己的子线程中处理消息,必须首先为子线程创建一个looper对象(Loooper.prepare(),然后初始化Handler并重写handleMessage(Message msg) 方法,最后启动通过Looper.loop()来启动Looper


最后我们用一张图来展示他们之间的关系:


现在我们来总结一下Handler的使用流程:


1、调用Looper.prepare(),为当前的线程创建Looper对象,在创建Looper对象的时候,会自动创建MessageQueue。

2、之后,创建Handler子类 的实例,重写handleMessage()方法,该方法用来处理来自于其他线程的消息。

3、调用Looper的loop()方法启动Looper。

(注意:主线程中只需要第2步)

比如说A线程需要传递数据给B线程,流程如下:

1、在B线程中首先调用Looper.prepare。(主线程不需要)

2、 编写Handler类,重写其中的handleMessage方法。

3、在B线程调用Looper.loop启动Looper.

4、创建Handler实例,在A线程中调用Handler的sentMessage方法发送消息。


附上简单的代码:

主线程Handler的定义:

mainHandler = new Handler() {private ImageView image;@Overridepublic void handleMessage(Message msg) {//逻辑代码}}};

子线程Handler的定义:

class ConnURLThread extends Thread {private Handler subHandler;@Overridepublic void run() {Looper.prepare();subHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {//逻辑代码}}};Looper.loop();}}




更多相关文章

  1. android 实现主线程、子线程双向通信
  2. Android中需要手动关闭的地方总结
  3. 十大技巧优化Android(安卓)App性能
  4. android手机开发课小结
  5. 从架构师的角度分析Android(安卓)Handler 源码的正确姿势
  6. 《第一行代码》学习笔记之服务
  7. Android中Handler,Looper,MessageQueue和Thread
  8. Android(安卓)中使用 dlib+opencv 实现动态人脸检测
  9. Android内存泄漏检测及修复(转载)

随机推荐

  1. 原理+代码|手把手教你使用Python实战反欺
  2. 用Python打造一款文件搜索工具,所有功能自
  3. 老板让我从几百个Excel中查找数据,我用Pyt
  4. 可视化工具不知道怎么选?深度评测5大Pytho
  5. 原理+代码|Python实战多元线性回归模型
  6. 原理+代码|Python基于主成分分析的客户信
  7. Python办公自动化 | 从PPT到Word
  8. 十分钟快速了解Pandas的常用操作!
  9. 原理 + 代码|手把手教你用Python实现智能
  10. 分享我学习Pandas使用的资料,可能是新手入