新技术论坛
搜索
查看: 865|回复: 0
打印 上一主题 下一主题

[Android] Android Handler原理详解

[复制链接]
  • TA的每日心情
    开心
    2016-12-9 18:18
  • 签到天数: 85 天

    连续签到: 1 天

    [LV.6]常住居民II

    扫一扫,手机访问本帖
    楼主
    跳转到指定楼层
    发表于 2016-4-19 06:36:28 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
    简介

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

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

    Handler

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

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

    3.     private TextView mTextView;
    4.     private Handler mHandler = new Handler() {
    5.         @Override
    6.         public void handleMessage(Message msg) {
    7.             switch (msg.what) {
    8.                 case MESSAGE_TEXT_VIEW:
    9.                     mTextView.setText("UI成功更新");
    10.                 default:
    11.                     super.handleMessage(msg);
    12.             }
    13.         }
    14.     };

    15.     @Override
    16.     protected void onCreate(Bundle savedInstanceState) {
    17.         super.onCreate(savedInstanceState);
    18.         setContentView(R.layout.activity_main);
    19.         Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    20.         setSupportActionBar(toolbar);

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

    22.         new Thread(new Runnable() {
    23.             @Override
    24.             public void run() {
    25.                 try {
    26.                     Thread.sleep(3000);
    27.                 } catch (InterruptedException e) {
    28.                     e.printStackTrace();
    29.                 }
    30.                 mHandler.obtainMessage(MESSAGE_TEXT_VIEW).sendToTarget();
    31.             }
    32.         }).start();

    33.     }
    34. }
    复制代码

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

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

    10.     mLooper = Looper.myLooper();
    11.     if (mLooper == null) {
    12.         throw new RuntimeException(
    13.             "Can't create handler inside thread that has not called Looper.prepare()");
    14.     }
    15.     mQueue = mLooper.mQueue;
    16.     mCallback = callback;
    17.     mAsynchronous = async;
    18. }
    复制代码

    在构造方法中,通过调用 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() 方法,下面是它的源码:

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

    2. public static @Nullable Looper myLooper() {
    3.     return sThreadLocal.get();
    4. }
    复制代码

    这个方法的代码很简单,就是从 sThreadLocal 中获取 Looper 对象。sThreadLocal 是 ThreadLocal 对象,这说明 Looper 是线程独立的。

    在 Handler 的构造中,从抛出的异常可知,每个线程想要获得 Looper 需要调用 prepare() 方法,继续看它的代码:

    1. private static void prepare(boolean quitAllowed) {
    2.     if (sThreadLocal.get() != null) {
    3.         throw new RuntimeException("Only one Looper may be created per thread");
    4.     }
    5.     sThreadLocal.set(new Looper(quitAllowed));
    6. }
    复制代码

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

    1. private Looper(boolean quitAllowed) {
    2.     mQueue = new MessageQueue(quitAllowed);
    3.     mThread = Thread.currentThread();
    4. }
    复制代码

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

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

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

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

    11.     //无限循环
    12.     for (;;) {
    13.         Message msg = queue.next(); // might block
    14.         if (msg == null) {
    15.             // No message indicates that the message queue is quitting.
    16.             return;
    17.         }

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

    24.         msg.target.dispatchMessage(msg);

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

    28.         // Make sure that during the course of dispatching the
    29.         // identity of the thread wasn't corrupted.
    30.         final long newIdent = Binder.clearCallingIdentity();
    31.         if (ident != newIdent) {
    32.             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);
    33.         }

    34.         msg.recycleUnchecked();
    35.     }
    36. }
    复制代码

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

    消息发送与处理

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

    1. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    2.     MessageQueue queue = mQueue;
    3.     if (queue == null) {
    4.         RuntimeException e = new RuntimeException(
    5.                 this + " sendMessageAtTime() called with no mQueue");
    6.         Log.w("Looper", e.getMessage(), e);
    7.         return false;
    8.     }
    9.     return enqueueMessage(queue, msg, uptimeMillis);
    10. }

    11. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    12.     msg.target = this;
    13.     if (mAsynchronous) {
    14.         msg.setAsynchronous(true);
    15.     }
    16.     return queue.enqueueMessage(msg, uptimeMillis);
    17. }
    18. 这个方法就是调用 enqueueMessage 在消息队列中插入一条消息,在 enqueueMessage总中,会把 msg.target 设置为当前的Handler 对象。

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

    20. public void dispatchMessage(Message msg) {
    21.     if (msg.callback != null) {
    22.         handleCallback(msg);
    23.     } else {
    24.         if (mCallback != null) {
    25.             if (mCallback.handleMessage(msg)) {
    26.                 return;
    27.             }
    28.         }
    29.         handleMessage(msg);
    30.     }
    31. }
    复制代码

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

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

    1. public final boolean post(Runnable r)
    2. {
    3.    return  sendMessageDelayed(getPostMessage(r), 0);
    4. }
    5. private static Message getPostMessage(Runnable r) {
    6.     Message m = Message.obtain();
    7.     m.callback = r;
    8.     return m;
    9. }
    复制代码

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

    1. private static void handleCallback(Message message) {
    2.     message.callback.run();
    3. }
    复制代码

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

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

    1. public interface Callback {
    2.     public boolean handleMessage(Message msg);
    3. }
    复制代码

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

    总结

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




    高级模式
    B Color Image Link Quote Code Smilies

    本版积分规则

    手机版|Archiver|开发者俱乐部 ( ICP/ISP证:辽B-2-4-20110106号 IDC证:辽B-1-2-20070003号 )

    GMT+8, 2025-1-4 03:19 , Processed in 0.120378 second(s), 22 queries .

    X+ Open Developer Network (xodn.com)

    © 2009-2017 沈阳讯网网络科技有限公司

    快速回复 返回顶部 返回列表