Android Handler工作原理解析

简介

在Android 中,只有主线程才能操作 UI,但是主线程不能进行耗时操作,否则会阻塞线程,产生 ANR 异常,所以常常把耗时操作放到其它子线程进行。如果在子线程中需要更新 UI,一般是通过 Handler 发送消息,主线程接受消息并且进行相应的逻辑处理。除了直接使用 Handler,还可以通过 View 的 post 方法以及 Activity 的 runOnUiThread 方法来更新 UI,它们内部也是利用了Handler 。在上一篇文章 Android AsyncTask源码分析 中也讲到,其内部使用了 Handler 把任务的处理结果传回 UI 线程。

本文深入分析 Android 的消息处理机制,了解 Handler 的工作原理。

Handler

先通过一个例子看一下 Handler 的用法。

public class MainActivity extends AppCompatActivity {
  private static final int MESSAGE_TEXT_VIEW = 0;

  private TextView mTextView;
  private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      switch (msg.what) {
        case MESSAGE_TEXT_VIEW:
          mTextView.setText("UI成功更新");
        default:
          super.handleMessage(msg);
      }
    }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    mTextView = (TextView) findViewById(R.id.text_view);

    new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          Thread.sleep(3000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        mHandler.obtainMessage(MESSAGE_TEXT_VIEW).sendToTarget();
      }
    }).start();

  }
}

上面的代码先是新建了一个 Handler的实例,并且重写了 handleMessage 方法,在这个方法里,便是根据接受到的消息的类型进行相应的 UI 更新。那么看一下 Handler的构造方法的源码:

public Handler(Callback callback, boolean async) {
  if (FIND_POTENTIAL_LEAKS) {
    final Class<? extends Handler> klass = getClass();
    if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
        (klass.getModifiers() & Modifier.STATIC) == 0) {
      Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
        klass.getCanonicalName());
    }
  }

  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() 获得了 Looper 对象。如果 mLooper 为空,那么会抛出异常:"Can't create handler inside thread that has not called Looper.prepare()",意思是:不能在未调用 Looper.prepare() 的线程创建 handler。上面的例子并没有调用这个方法,但是却没有抛出异常。其实是因为主线程在启动的时候已经帮我们调用过了,所以可以直接创建 Handler 。如果是在其它子线程,直接创建 Handler 是会导致应用崩溃的。

在得到 Handler 之后,又获取了它的内部变量 mQueue, 这是 MessageQueue 对象,也就是消息队列,用于保存 Handler 发送的消息。

到此,Android 消息机制的三个重要角色全部出现了,分别是 Handler 、Looper 以及 MessageQueue。 一般在代码我们接触比较多的是 Handler ,但 Looper 与 MessageQueue 却是 Handler 运行时不可或缺的。

Looper

上一节分析了 Handler 的构造,其中调用了 Looper.myLooper() 方法,下面是它的源码:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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

这个方法的代码很简单,就是从 sThreadLocal 中获取 Looper 对象。sThreadLocal 是 ThreadLocal 对象,这说明 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));
}

同样很简单,就是给 sThreadLocal 设置一个 Looper。不过需要注意的是如果 sThreadLocal 已经设置过了,那么会抛出异常,也就是说一个线程只会有一个 Looper。创建 Looper 的时候,内部会创建一个消息队列:

private Looper(boolean quitAllowed) {
  mQueue = new MessageQueue(quitAllowed);
  mThread = Thread.currentThread();
}

现在的问题是, Looper看上去很重要的样子,它到底是干嘛的?
回答: Looper 开启消息循环系统,不断从消息队列 MessageQueue 取出消息交由 Handler 处理。

为什么这样说呢,看一下 Looper 的 loop方法:

public static void loop() {
  final Looper me = myLooper();
  if (me == null) {
    throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  }
  final MessageQueue queue = me.mQueue;

  // 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 (;;) {
    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
    Printer logging = me.mLogging;
    if (logging != null) {
      logging.println(">>>>> Dispatching to " + msg.target + " " +
          msg.callback + ": " + msg.what);
    }

    msg.target.dispatchMessage(msg);

    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();
  }
}

这个方法的代码有点长,不去追究细节,只看整体逻辑。可以看出,在这个方法内部有个死循环,里面通过 MessageQueue 的next() 方法获取下一条消息,没有获取到会阻塞。如果成功获取新消息,便调用 msg.target.dispatchMessage(msg),msg.target是 Handler 对象(下一节会看到),dispatchMessage 则是分发消息(此时已经运行在 UI 线程),下面分析消息的发送及处理流程。

消息发送与处理

在子线程发送消息时,是调用一系列的 sendMessage、sendMessageDelayed 以及 sendMessageAtTime 等方法,最终会辗转调用sendMessageAtTime(Message msg, long uptimeMillis),代码如下:

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);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  msg.target = this;
  if (mAsynchronous) {
    msg.setAsynchronous(true);
  }
  return queue.enqueueMessage(msg, uptimeMillis);
}

这个方法就是调用 enqueueMessage 在消息队列中插入一条消息,在 enqueueMessage总中,会把 msg.target 设置为当前的Handler 对象。

消息插入消息队列后, Looper 负责从队列中取出,然后调用 Handler 的 dispatchMessage 方法。接下来看看这个方法是怎么处理消息的:

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

首先,如果消息的 callback 不是空,便调用 handleCallback 处理。否则判断 Handler 的 mCallback 是否为空,不为空则调用它的 handleMessage方法。如果仍然为空,才调用 Handler 自身的 handleMessage,也就是我们创建 Handler 时重写的方法。

如果发送消息时调用 Handler 的 post(Runnable r)方法,会把 Runnable封装到消息对象的 callback,然后调用sendMessageDelayed,相关代码如下:

public final boolean post(Runnable r)
{
  return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
  Message m = Message.obtain();
  m.callback = r;
  return m;
}

此时在 dispatchMessage中便会调用 handleCallback进行处理:

 private static void handleCallback(Message message) {
  message.callback.run();
}

可以看到是直接调用了 run 方法处理消息。

如果在创建 Handler时,直接提供一个 Callback 对象,消息就交给这个对象的 handleMessage 方法处理。Callback 是 Handler内部的一个接口:

public interface Callback {
  public boolean handleMessage(Message msg);
}

以上便是消息发送与处理的流程,发送时是在子线程,但处理时 dispatchMessage 方法运行在主线程。

总结

至此,Android消息处理机制的原理就分析结束了。现在可以知道,消息处理是通过 Handler 、Looper 以及 MessageQueue共同完成。 Handler 负责发送以及处理消息,Looper 创建消息队列并不断从队列中取出消息交给 Handler, MessageQueue 则用于保存消息。

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

(0)

相关推荐

  • android Handler详细使用方法实例

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

  • Android多线程处理机制中的Handler使用介绍

    接下来让我介绍Android的Handler的使用方法.Handler可以发送Messsage和Runnable对象到与其相关联的线程的消息队列.每个Handler对象与创建它的线程相关联,并且每个Handler对象只能与一个线程相关联. Handler一般有两种用途:1)执行计划任务,你可以再预定的实现执行某些任务,可以模拟定时器.2)线程间通信.在Android的应用启动时,会创建一个主线程,主线程会创建一个消息队列来处理各种消息.当你创建子线程时,你可以再你的子线程中拿到父线程中创建的Ha

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

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

  • 详解Android中Handler的使用方法

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

  • Handler与Android多线程详解

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

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

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

  • 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使用handlerthread创建线程示例

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

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

随机推荐