android线程消息机制之Handler详解

android线程消息机制主要由Handler,Looper,Message和MessageQuene四个部分组成。平常在开发中,我们常用来在子线程中通知主线程来更新,其实整个安卓生命周期的驱动都是通过Handler(ActivityThread.H)来实现的。

首先我们先介绍这四个类的作用:

Handler:消息的发送者。负责将Message消息发送到MessageQueue中。以及通过Runnable,Callback或者handleMessage()来实现消息的回调处理

Looper:是消息的循环处理器,它负责从MessageQueue中取出Message对象进行处理。(Looper含有MessageQueue的引用)

Message:是消息载体,通过target来指向handler的引用。通过object来包含业务逻辑数据。其中MessagePool为消息池,用于回收空闲的Message对象的。

MessageQueue:消息队列,负责维护待处理的消息对象。

通过上面的图,我们可以比较清楚地知道他们的作用以及关系。接下来,我们从源码角度来分析这种关系是如何建立的。

public Handler(Looper looper, Callback callback, boolean async) {
  mLooper = looper;
  mQueue = looper.mQueue;
  mCallback = callback;
  mAsynchronous = async;
}

hander的其它构造方法可以自己去查看,通过这个构造方法,我们知道,handler持有MessageQueue的引用。所以可以方便地将Message加入到队列中去。

通过源码我们发现,sendMessage->sendMessageDelayed->sendMessageAtTime->enqueueMessage

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  MessageQueue queue = mQueue;
  if (queue == null) {
    RuntimeException e = new RuntimeException(
        this + " sendMessageAtTime() called with no mQueue");
    Log.w("Looper", e.getMessage(), e);
    return false;
  }
  return enqueueMessage(queue, msg, uptimeMillis);
}

都是通过enqueueMessage将message将加入到MessageQueue中。

接下来,我们看Message是如何构造的。通过Message的构造方法。

public static Message obtain() {
  synchronized (sPoolSync) {
    if (sPool != null) {
      Message m = sPool;
      sPool = m.next;
      m.next = null;
      m.flags = 0; // clear in-use flag
      sPoolSize--;
      return m;
    }
  }
  return new Message();
}

我们看到,Message是通过obtain的静态方法从消息池sPool中拿到的。这样可以做到消息的复用。

public static Message obtain(Handler h) {
  Message m = obtain();
  m.target = h;

  return m;
}

其中有一个重载方法中m.target = h;这段代码非常重要,便于后面找到消息的目标handler进行处理。

接下来,我们来看Looper。我们知道Looper通过过Looper.loop来进入循环的,而循环是通过线程的run方法的驱动的。

首先我们知道,我们在创建Handler的时候,都没有去创建Looper,那么Looper哪里来的呢?

public Handler(Callback callback, boolean async) {
    ...
    mLooper = Looper.myLooper();
    if (mLooper == null) {
      throw new RuntimeException(
        "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
  }

再看看Looper.myLooper()

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
  }

ThreadLocal是线程创建线程局部变量的类。表示此变量只属于当前线程。

  public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
      ThreadLocalMap.Entry e = map.getEntry(this);
      if (e != null) {
        @SuppressWarnings("unchecked")
        T result = (T)e.value;
        return result;
      }
    }
    return setInitialValue();
  }

我们看到了sThreadLocal.get()的方法实际是取当前线程中的Looper对象。

那么我们主线程的Looper到底在哪里创建的呢?
而我们清楚地知道,如果在子线程中创建handler调用,则需要使用Looper.prepare方法。

  private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
      throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
  }

我们看到此方法中,如果此线程中没有Looper对象,则创建一个Looper对象。接下来我们在源码中看到一个熟悉的方法。

  public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
      if (sMainLooper != null) {
        throw new IllegalStateException("The main Looper has already been prepared.");
      }
      sMainLooper = myLooper();
    }
  }

此方法单独的创建了一个sMainLooper用于主线程的Looper。这个prepareMainLooper到底在哪里调用呢?

高过引用指向发现,我们在ActivityThread.main()方法中发现

  public static void main(String[] args) {
    ...
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
      sMainThreadHandler = thread.getHandler();
    }

    if (false) {
      Looper.myLooper().setMessageLogging(new
          LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
  }

而ActivityThread.main()是程序的入口方法。这样我们就非常清楚了,主线程的Looper在程序的启动过程中就已经创建并循环。

那么如果在子线程中创建Looper该如何正确调用呢?

class LooperThread extends Thread {
   public Handler mHandler;

   public void run() {
     Looper.prepare();

     mHandler = new Handler() {
       public void handleMessage(Message msg) {
         // process incoming messages here
       }
     };

     Looper.loop();
   }
 }

接下来,我们需要看下Looper.loop()的执行方法

public static void loop() {
    final Looper me = myLooper();//拿到当前线程的looper
    if (me == null) {
      throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;//拿到当前looper的消息队列

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {//死循环遍历消息体。如果为null,则休眠。
      Message msg = queue.next(); // might block
      if (msg == null) {
        // No message indicates that the message queue is quitting.
        return;
      }

      // This must be in a local variable, in case a UI event sets the logger
      final Printer logging = me.mLogging;
      if (logging != null) {
        logging.println(">>>>> Dispatching to " + msg.target + " " +
            msg.callback + ": " + msg.what);
      }

      final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

      final long traceTag = me.mTraceTag;
      if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
        Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
      }
      final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
      final long end;
      try {
        msg.target.dispatchMessage(msg);//此处是真正的分发消息。此处的target即是handler对象
        end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
      } finally {
        if (traceTag != 0) {
          Trace.traceEnd(traceTag);
        }
      }
      if (slowDispatchThresholdMs > 0) {
        final long time = end - start;
        if (time > slowDispatchThresholdMs) {
          Slog.w(TAG, "Dispatch took " + time + "ms on "
              + Thread.currentThread().getName() + ", h=" +
              msg.target + " cb=" + msg.callback + " msg=" + msg.what);
        }
      }

      if (logging != null) {
        logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
      }

      // Make sure that during the course of dispatching the
      // identity of the thread wasn't corrupted.
      final long newIdent = Binder.clearCallingIdentity();
      if (ident != newIdent) {
        Log.wtf(TAG, "Thread identity changed from 0x"
            + Long.toHexString(ident) + " to 0x"
            + Long.toHexString(newIdent) + " while dispatching to "
            + msg.target.getClass().getName() + " "
            + msg.callback + " what=" + msg.what);
      }

      msg.recycleUnchecked();
    }
  }

最后我们看下dispatchMessage的处理方法。

  public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
      handleCallback(msg);
    } else {
      if (mCallback != null) {
        if (mCallback.handleMessage(msg)) {
          return;
        }
      }
      handleMessage(msg);
    }
  }

我们看到,dispatchMessage是优化处理msg.callback,然后就是实现的Callback接口,最后才是handleMessage方法。

重点说明:

1、handler在实例化的时候,持有Looper的引用。是通过ThreadLocal与Handler进行关联的。

2、Message在实例化的过程中,通过target 持有Handler的引用。

3、通常一个线程对应一个Looper.一个Looper可以属于多个Handler。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android开发笔记之:Handler Runnable与Thread的区别详解

    在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类,这是此方法的局限.下面看例子: 复制代码 代码如下: package org.thread.demo; class MyThread extends Thread{ private String name; public MyThread(St

  • Android Handler之消息循环的深入解析

    Handler是用于操作线程内部的消息队列的类.这有点绕,没关系,我们慢慢的来讲.前面Looper一篇讲到了Looper是用于给线程创建消息队列用的,也就是说Looper可以让消息队列(MessageQueue)附属在线程之内,并让消息队列循环起来,接收并处理消息.但,我们并不直接的操作消息队列,而是用Handler来操作消息队列,给消息队列发送消息,和从消息队列中取出消息并处理.这就是Handler的职责.Handler,Looper和MessageQueue是属于一个线程内部的数据,但是它提

  • android使用handlerthread创建线程示例

    在android开发中,一说起线程的使用,很多人马上想到new Thread(){...}.start()这种方式.这样使用当然可以,但是多次使用这种方式,会创建多个匿名线程.使得程序运行起来越来越慢.因此,可以考虑使用一个Handler来启动一个线程,当该线程不再使用就删除,保证线程不会重复创建.一般会使用Handler handler = new Handler(){...}创建Handler.这样创建的handler是在主线程即UI线程下的Handler,即这个Handler是与UI线程下

  • 详解Android中Handler的使用方法

    在Android开发中,我们经常会遇到这样一种情况:在UI界面上进行某项操作后要执行一段很耗时的代码,比如我们在界面上点击了一个"下载"按钮,那么我们需要执行网络请求,这是一个耗时操作,因为不知道什么时候才能完成.为了保证不影响UI线程,所以我们会创建一个新的线程去执行我们的耗时的代码.当我们的耗时操作完成时,我们需要更新UI界面以告知用户操作完成了.所以我们可能会写出如下的代码: package ispring.com.testhandler; import android.app.

  • Android Handler主线程和一般线程通信的应用分析

    Handler的定义:主要接受子线程发送的数据, 并用此数据配合主线程更新UI.解释: 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件,进行事件分发, 比如说, 你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作.如果此时需要一个耗时的操作,例如: 联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会

  • android Handler详细使用方法实例

    开发环境为android4.1.Handler使用例1这个例子是最简单的介绍handler使用的,是将handler绑定到它所建立的线程中.本次实验完成的功能是:单击Start按钮,程序会开始启动线程,并且线程程序完成后延时1s会继续启动该线程,每次线程的run函数中完成对界面输出nUpdateThread...文字,不停的运行下去,当单击End按钮时,该线程就会停止,如果继续单击Start,则文字又开始输出了. 软件界面如下: 实验主要部分代码和注释: MainActivity.java: 复

  • android的消息处理机制(图文+源码分析)—Looper/Handler/Message

    这篇文章写的非常好,深入浅出,关键还是一位大三学生自己剖析的心得.这是我喜欢此文的原因.下面请看正文: 作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想.android源码中包含了大量的设 计模式,除此以外,android sdk还精心为我们设计了各种helper类,对于和我一样渴望水平得到进阶的人来说,都太值得一读了.这不,前几天为了了解android的消息处理机 制,我看了Looper,Handler,Message这几个类的源码,结果又

  • Android定时器和Handler用法实例分析

    本文实例讲述了Android定时器和Handler用法.分享给大家供大家参考.具体分析如下: 一.环境: 主机:WIN8 开发环境:Android Studio 二.定时器使用示例: 初始化: //定时器 private Timer Timer_Work = new Timer(); //工作间隔,单位:ms private final int INTERVAL_WORK = 5000; 创建定时器线程: /** * 构造函数 */ public Config() { //生成配置信息 gene

  • 详解Android中Handler的内部实现原理

    本文主要是对Handler和消息循环的实现原理进行源码分析,如果不熟悉Handler可以参见博文<详解Android中Handler的使用方法>,里面对Android为何以引入Handler机制以及如何使用Handler做了讲解. 概括来说,Handler是Android中引入的一种让开发者参与处理线程中消息循环的机制.我们在使用Handler的时候与Message打交道最多,Message是Hanlder机制向开发人员暴露出来的相关类,可以通过Message类完成大部分操作Handler的功

  • Handler与Android多线程详解

    下面是一段大家都比较熟悉的代码: 复制代码 代码如下: Handler handler = new Handler(); handler.post(myThread); //使用匿名内部类创建一个线程myThreadRunnable mythread = new Runnable() {public void run() {}}; 一开始,相信很多人都以为myThread中的run()方法会在一个新的线程中运行,但事实并非如此. 上述代码中的handler并没有调用线程myThread的star

随机推荐