Android----Handler机制剖析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SiwenYY/article/details/77095023
       为了避免ANR,我们会通常把耗时操作放在子线程里面去执行,因为子线程不能更新UI,所以当子线程需要更新的UI的时候就需要借助到安卓的消息机制,也就是Handler机制。

 
 
主线程: 也叫UI线程,或称ActivityThread,用于运行四大组件和处理他们用户的交互。
 ActivityThread管理应用进程的主线程的执行(相当于普通Java程序的main入口函数),在Android系统中,在默认情况下,一个应用程序内的各个组件(如Activity、BroadcastReceiver、Service)都会在同一个进程(Process)里执行,且由此进程的主线程负责执行。 ActivityThread既要处理Activity组件的UI事件,又要处理Service后台服务工作,通常会忙不过来。为了解决此问题,主线程可以创建多个子线程来处理后台服务工作,而本身专心处理UI画面的事件。
子线程: 用于执行耗时操作,比如 I/O操作和网络请求等。(安卓3.0以后要求耗访问网络必须在子线程种执行)更新UI的工作必须交给主线程,子线程在安卓里是不允许更新UI的。
1.在讲Handler机制之前,首先介绍下以下几个对象

Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。

Handler:处理者,负责将Message添加到消息队列以及对消息队列中的Message进行处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。

MessageQueue:消息队列,用来存放Handler发送过来的消息,按照先进先出执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。

Looper: 循环器,扮演Message Queue和Handler之间桥梁的角色,循环取出Message Queue里面的Message,并交付给相应的Handler进行处理。因此,一个MessageQueue需要一个Looper。

Thread:线程,负责调度整个消息循环,即消息循环的执行场所。

2.接下来讲讲Handler,Looper和MessageQueue这三者的关系

Looper和MessageQueue一一对应,创建一个Looper的同时,会创建一个MessageQueue。

Handler与它们的关系,只是简单的聚集关系,即Handler里会引用当前线程里的特定Looper和MessageQueue。

这样说来,多个Handler都可以共享同一Looper和MessageQueue了。

这些Handler也就运行在同一个线程里,每个线程一个Loop,一个MessageQueue。

3.Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。

3.1 Handler的定义:主要接受子线程发送的数据, 并用此数据配合主线程更新UI。

解释:当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件, 进行事件分发, 比如说, 你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。 如果此时需要一个耗时的操作,例如: 联网读取数据, 或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会收到 Android系统的一个错误提示 "强制关闭"。 这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,,Android主线程是线程不安全的, 也就是说,更新UI只能在主线程中更新,子线程中操作是危险的。 这个时候,Handler就出现了。,来解决这个复杂的问题 ,由于Handler运行在主线程中(UI线程中), 它与子线程可以通过Message对象来传递数据, 这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据) , 把这些消息放入主线程队列中,配合主线程进行更新UI。
3.2 Handler一些特点
 handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程),它有两个作用:
 (1)安排消息或Runnable 在某个主线程中某个地方执行;
 (2)安排一个动作在不同的线程中执行。
3.3 Handler实例
 子类需要继承
Handler 类,并重写handleMessage(Message msg) 方法, 用于接受线程数据
4.1 创建消息:
Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。
使用消息池的好处:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。
4.2 发送消息:
UI 主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该Looper与UI主线程一一对应。
使用ThreadLocal的目的是保证每一个线程只创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的Looper。
Looper初始化 的时候会创建一个 消息队列MessageQueue。
Hander持有对UI主线程消息队列MessageQueue和消息循环Looper的引用,子线程可以通过 Handler将消息发送到UI线程的消息队列MessageQueue中。
4.3 处理消息:
UI主线程通过Looper循环查询消息队列,当发现有消息存在时会将消息从消息队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。
5.Handler机制使用流程
5.1 Handler机制是Android提供的一种异步回调机制,在Ui线程中使用handler需要new一个handler对象,并重写其中的handleMessage(Message msg)方法,处理如更新UI等操作。
 
   
  1. private Handler handler = new Handler(){
  2. @Override
  3. public void handleMessage(Message msg) {
  4. //to do
  5. }
  6. };

5.2 在异步线程中调用hanlder.sendMessage(Message msg)告诉UI线程去更新UI等操作。
从原理上来讲,使用了Hanlder对象的线程需要绑定一个Looper对象,该对象维护一个消息队列,Looper对象取出该队列的消息后交由handler进行处理。所以在使用Handler时,要调用Looper.prepare()法给当前的线程绑定一个Looper对象。
 
   
  1.    public static void prepare() {
  2. prepare(true);
  3. }
  4. private static void prepare(boolean quitAllowed) {
  5. if (sThreadLocal.get() != null) {
  6. throw new RuntimeException("Only one Looper may be created per thread");
  7. }
  8. sThreadLocal.set(new Looper(quitAllowed));
  9. }

然后调用loop()建立对消息队列的循环,在消息队列中取出消息后交由相应的handler进行处理。由于一个线程中可能有多个handler,为了区分这些不同的hanlder所需要处理的消息,每个Message对象都维护有一个hanlder实例即target,在loop方法中通过调用msg.target.dispatchMessage(msg)进行处理。

每个线程只能绑定一个loop对象,多个handler共享,handler的在构造时从当前的线程中取得loop对象,Looper中的myLooper中返回了sThreadLocal.get()所取得的Looper对象

 
     
  1. mLooper = Looper.myLooper();
  2. if (mLooper == null) {
  3. throw new RuntimeException(
  4. "Can't create handler inside thread that has not called Looper.prepare()");
  5. }
  6. mQueue = mLooper.mQueue;
所以,在使用handler中需要执行以下步骤,首先调用Looper.prepare()方法为当前线程绑定Looper对象,然后才可以实例化一个Handler对象,最后调用loop()方法建立消息循环
 
     
  1. class LooperThread extends Thread {
  2. public Handler mHandler;
  3. public void run() {
  4. Looper.prepare();
  5. mHandler = new Handler() {
  6. public void handleMessage(Message msg) {
  7. // process incoming messages here
  8. }
  9. };
  10. Looper.loop();
  11. }
  12. }
在很多情况下我们在activity或者其他场合中使用Handler进行异步消息处理,并没有显式地声明绑定一个Looper对象。这是因为主线程它已经绑定了一个Looper对象
 
     
  1. public static final void main(String[] args) {
  2. SamplingProfilerIntegration.start();
  3. Process.setArgV0("<pre-initialized>");
  4. Looper.prepareMainLooper();
  5. ActivityThread thread = new ActivityThread();
  6. thread.attach(false);
  7. Looper.loop();
  8. if (Process.supportsProcesses()) {
  9. throw new RuntimeException("Main thread loop unexpectedly exited");
  10. }
  11. thread.detach();
  12. String name = (thread.mInitialApplication != null)
  13. ? thread.mInitialApplication.getPackageName()
  14. : "<unknown>";
  15. Slog.i(TAG, "Main thread of " + name + " is now exiting");
  16. }





想更清楚的了解Handler机制,请参考以下资料:

猜你喜欢

转载自blog.csdn.net/SiwenYY/article/details/77095023