Android——Handler

2019-10-14  本文已影响0人  四喜汤圆

一、作用

轻松地将一个任务切换到 Handler 所在线程去执行。可用于发送消息和处理消息。

二、概念

0. Android 消息机制及源码分析

Android 消息机制

一个线程中可有多个 Handler。

Looper 中维护一个 MessageQueue,一个线程只有一个 MessageQueue

每个线程只能有一个 Looper,Looper 保存在 ThreadLocal 中。主线程已经创建了 Looper 不需要再手动创建,其他线程的 Looper 需手动创建。

1. 发送消息 API

Android:这是一份Handler消息传递机制 的使用教程
消息的发送有 2 种方式
(1)post 的一系列方法

/**
 * 将runnable添加到消息队列中, runnable 将在该Handler绑定的thread运行
 * 
 * @param r
 * @return
 */
public final boolean post(Runnable r) {
    return sendMessageDelayed(getPostMessage(r), 0);
}

/**
 * 将runnable添加到消息队列中,在指定时间(<var>uptimeMillis</var>)运行, runnable 将被该Handler绑定的thread运行
 * 
 * @param r
 * @param uptimeMillis @return:true:当runnable被正确添加到消息队列中;false:通常是由于处理消息队列的Looper正在退出。
 * 注意:返回值为true,不代表runnable会被执行(例如:在消息被执行之前Looper退出了,那么该消息将被dropped丢弃)
 */
public final boolean postAtTime(Runnable r, long uptimeMillis) {
    return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}

/**
 * 将runnable添加到消息队列中,在指定时间(<var>uptimeMillis</var>)运行,
 * 
 * @param r
 * @param token
 * @param uptimeMillis
 * @return
 */
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
    return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}

/**
 * 将runnable添加到消息队列,在指定时间后执行
 * 
 * @param r
 * @param delayMillis
 * @return
 */
public final boolean postDelayed(Runnable r, long delayMillis) {
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

/**
 * 这个方法在极特殊的情况下才调用,可能导致side-effects
 * 
 * @param r
 * @return
 */
public final boolean postAtFrontOfQueue(Runnable r) {
    return sendMessageAtFrontOfQueue(getPostMessage(r));
}

(2)send 的一系列方法

public final boolean sendMessage(Message msg) {
    return sendMessageDelayed(msg, 0);
}

public final boolean sendEmptyMessage(int what) {
    return sendEmptyMessageDelayed(what, 0);
}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageAtTime(msg, uptimeMillis);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

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

public final boolean sendMessageAtFrontOfQueue(Message msg) {
    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, 0);
}

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

2. 移除消息

????

/**
 * Remove any pending posts of Runnable r that are in the message queue.
 */
public final void removeCallbacks(Runnable r) {
    mQueue.removeMessages(this, r, null);
}

/**
 * Remove any pending posts of Runnable <var>r</var> with Object
 * <var>token</var> that are in the message queue.  If <var>token</var> is null,
 * all callbacks will be removed.
 */
public final void removeCallbacks(Runnable r, Object token) {
    mQueue.removeMessages(this, r, token);
}

/**
 * Remove any pending posts of messages with code 'what' that are in the
 * message queue.
 */
public final void removeMessages(int what) {
    mQueue.removeMessages(this, what, null);
}

/**
 * Remove any pending posts of messages with code 'what' and whose obj is
 * 'object' that are in the message queue.  If <var>object</var> is null,
 * all messages will be removed.
 */
public final void removeMessages(int what, Object object) {
    mQueue.removeMessages(this, what, object);
}

/**
 * Remove any pending posts of callbacks and sent messages whose
 * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
 * all callbacks and messages will be removed.
 */
public final void removeCallbacksAndMessages(Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}

三、重点了解

1. Q:为什么在没有 Looper 的线程中创建 Handler 会引起异常

Handler 的构造函数

public Handler() {
    this(null, false);
}

public Handler(Callback callback) {
    this(callback, false);
}

public Handler(Looper looper) {
    this(looper, null, false);
}

public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
}

public Handler(boolean async) {
    this(null, async);
}

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默认是同步的,除非通过参数async明确指定它是异步的
 * @param looper:使用提供的 Looper 而不是默认的Looper,必须不为空
 * @param callback:使用提供的callback处理消息,可以为null
 * @param async:
 */
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

通过2个参数的构造函数可知,如果当前线程没有 Looper 的话,就会抛出异常,这也就解释了在没有 Looper 的子线程创建 Handler 会引起异常。

2. Q:Handler 如何处理消息

对消息的处理最终会回调到 Handler 的dispatchMessage()中。

/**
 * Handle system messages here.
 */
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();
}
/**
 * 这是一个接口,可以在实例化 Handler 时使用。
 * 好处:在日常开发中,创建 Handler 的常见方式是派生一个 Handler 子类并重写 handlerMessage()来处理具体的消息。
 * 有了 Callback,为我们创建 Handler 提供了另外一种可用方式,使得我们不想派生子类时,可以通过 Callback 实现。
 *
 * @param msg A {@link android.os.Message Message} object
 * @return True if no further handling is desired
 */
public interface Callback {
    public boolean handleMessage(Message msg);
}

Handler 提供了带Callback的构造函数,mCallback即为构造函数中传入的。

/**
 * 该构造函数将 Handler 和当前线程联系起来,Callback用于处理消息
 *
 * 如果当前线程没有 Looper,Handler 不可接收消息,并且会抛出异常
 *
 * @param callback The callback interface in which to handle messages, or null.
 */
public Handler(Callback callback) {
    this(callback, false);
}

mCallback不为空:调用mCallback.handleMessage(msg)
mCallback为空:调用Handler::handleMessage(msg)(该方法由 Handler 派生的子类重写,以处理消息)。

3. 获取 Message 的2种方式的比较

或许你可以从这个角度去理解Handler

2种方式

推荐使用Message.obtain():从全局消息池中返回一个新的消息实例,在多数情况下可以避免分配新对象。

三、使用

1. Handler 的创建

// 在主线程中创建实例
private MyHandler mHandler = new MyHandler();

private static class MyHandler extends Handler {
    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case 1:
                break;
            default:
                break;

        }
    }
}
// 在主线程中创建
private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        System.out.println(msg.what);
    }
};

2.发送消息

mHandler.post(new Runnable() {
    @Override
    public void run() {
        ... // 需在目标线程中执行的操作(如:更新UI)

    }

});
// 实例化消息对象
Message msg = Message.obtain();
msg.what = 1; // 消息标识
msg.obj = "AA"; // 消息内容存放

mHandler.sendMessage(msg);

参考文献

Handler源码
Message源码

上一篇下一篇

猜你喜欢

热点阅读