关于 Android 异步消息处理机制 Handler

详细的介绍可以看洋神的 Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系,我在这里只是简单的回顾一下。

一图胜千言

Handler 机制

Looper.prepare() 创建线程相关的 Looper 和 MessageQueue,Handler 将消息放入 MessageQueue,Looper.loop() 循环读取 Message 并交由 Handler 进行处理。

Handler 的基本用法

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

比较简单的用法:

  1. 调用 Looper.prepare() 来初始化 Looper
  2. 创建 Handler,通过 handleMessage 来处理消息
  3. 启动消息循环 Looper.loop()

然后就可以通过 mHandler 来向该线程发送消息了,也就实现了线程间的消息传递。下面来简单走读一下源码。

Looper

public final class Looper {
    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;

    public static void prepare() {
        prepare(true);
    }

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

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

    /**
     * Returns the application's main looper, which lives in the main thread of the application.
     */
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    ...

Looper 主要方法就这几个,构造方法是私有的,提供了静态方法供我们创建实例。从 Looper.prepare 可以看到有初始化 Looper,并将其放入了 ThreadLocal,且保证一个线程只能持有一个 Looper。MessageQueue 是在 Looper 构造方法里创建的,所以是一对一绑定的。

prepareMainLooper 也是初始化 Looper 的,只是它比较特殊,创建的是 UI 线程的 Looper,并且不需要我们手动调用,这也就是为什么在 UI 线程不需要再初始化 Looper 了。通过 Android Studio 的 Find Usages [option + F7]功能可以轻松找到 ActivityThread.main()方法里初始化了 MainLooper。

Looper.prepareMainLooper();
            ...

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

Looper.loop();

Looper.loop() 的作用是是启动消息循环,分发 msg

 /**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the 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;
    ...
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        msg.target.dispatchMessage(msg);
        ...
    }
}

从代码上看也比较容易理解,从消息队列中获取消息,然后通过 msg.target.dispatchMessage(msg) 分发处理,留下的问题就是

  1. msg 怎么进入的 MessageQueue
  2. msg.target 是谁

Handler

一般情况下我们是通过 Handler handler = new Handler() 来创建 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;
}

可以看到 Handler 默认关联当前线程的 Looper,当然也可以指定其他 Looper。

当通过 Handler.sendMessage 发送消息时,最后调用了 enqueueMessage

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

可以看到 msg.target = this,也就是当前的 Handler,而 Looper.loop 中分发消息的时候 msg.target.dispatchMessage(msg) 也就将消息交回了 Handler 进行处理。

最终 Message 分发的方式

Handler.dispatchMessage

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

private static void handleCallback(Message message) {
    message.callback.run();
}
  1. 带 callback 的 msg , 直接调用了 run
  2. mCallback != null, 也就是通过非空构造方法创建的 Handler
  3. 继承 Handler 的子类实现的 handleMessage 方法

Activity.runOnUiThread

runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //
            }
        });

这是一种常用的给 UI 线程发消息的方式

public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

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

代码也非常好理解,在 UI 线程的话直接调用 run 方法,非 UI 线程则 postMessage,对应 Handler.dispatchMessage 的第一种处理方法,还是直接调用 run 方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值