Android Handler 总结

什么是 Handler

handler 是通过发送和处理 Message 和 Runnable 对象来关联相对应线程的 MessageQueue。
每个 Android 应用在启动的时候,都会创建一个线程,这个线程成为主线程或者 UI 线程,Android 应用的所有操作默认都会在这个线程中执行。为了保证 UI 的流畅性,通常会将耗时操作(IO操作,网络请求等)放到子线程,如果在子线程中,我们又需要更新UI怎么办呢?Handler 的作用就显现出来了,虽然,它不仅仅是这样的作用。

A Handler allows you to send and process Message and **Runnable
objects** associated with(与…相联系/对应) a thread’s MessageQueue.

Each Handler instance is associated with a single thread and that thread’s message
queue. When you create a new Handler, it is bound to(绑定到) the thread /
message queue of the thread that is creating it – from that point on,
it will deliver messages and runnables to that message queue and execute
them as they come out of the message queue.

1、可以让对应的 Message 和 Runnable 在未来的某个时间点进行相应处理
2、让自己想要处理的耗时操作放在子线程,让更新 ui 的操作放在主线程

Handler 使用

post(runnable)

方法定义

// Causes the Runnable r to be added to the message queue. 
// The runnable will be run on the thread to which this handler is attached.
// 如果 Looper 所在线程的消息队列(MessageQueue)已经退出,那么发送会失败
public final boolean post (Runnable r)
{
    // 内部其实调用的也是 sendMessage 方法
    return  sendMessageDelayed(getPostMessage(r), 0);
}

// 将 runnable 作为 Message 的 callback 返回,无需我们再手动处理 handlMessage()
private static Message getPostMessage(Runnable r) {
    // 从全局池中返回新的消息实例。允许我们在许多情况下避免分配新对象。
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

简单使用

public class HandlerWithPostActivity extends AppCompatActivity {

    private final String TAG = getLocalClassName();
    // 创建 Handler,用于处理消息和发送消息
    private Handler mHandler = new Handler();
    private TextView mTextView;

    private void downLoad() {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                // 模拟下载任务
                Log.e(TAG, "down start");
                try {
                    // 模拟耗时操作
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 模拟下载完成
                Log.e(TAG, "downLoad end");

                Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        // 下载完成,更新 UI    
                        HandlerWithPostActivity.this.mTextView.setText(R.string.text_down_load_success);
                    }
                };
                // 调用 post 方法,将 runnable 发送给在 UI 线程的 mHandler 进行处理,达到在 UI 线程更新 UI 的目的
                mHandler.post(runnable);
            }
        });
        thread.start();
    }


}

sendMessage()

public class HandlerWithMessageActivity extends AppCompatActivity {

    private static final int WHAT_MESSAGE = 1;
    private TextView mTextView;
    // 正式环境不要这样操作,会引发内存泄漏,因为匿名内部类持有了 Activity 的引用
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // 处理消息
            switch (msg.what) {
                case 1:
                    mTextView.setText("延迟5秒显示消息");
                    break;
                default:
                    break;
            }
        }
    };

    private void showMessageDelay() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                // 获取消息,发送消息
                Message message = Message.obtain();
                message.what = WHAT_MESSAGE;
                mHandler.sendMessage(message);

                // 如果只是发送一个 what 的话,可以直接使用 sendMessage(WHAT_MESSAGE)
            }
        }).start();
    }
}

Handler 机制原理

Message

创建一个可以发送给 Handler ,包含描述信息和任意对象数据的 Message,Message 默认 包含两个 int 类型实例变量 (arg0,arg1)和一个任意类型对象变量 obj,使得我们大部分情况下,无需额外创建变量。

官方推荐的获取 Message 对象的方式是使用:Message.obtain(),或者是 Handler.obtainMessage(),这两中方式取得的对象都是从 缓存池 中拿的,避免创建新对象。

源码阅读

以 Handler 的构造方法为入口

/*
 * Set this flag to true to detect anonymous, local or member classes
 * that extend this Handler class and that are not static. These kind
 * of classes can potentially create leaks.
 */
private static final boolean FIND_POTENTIAL_LEAKS = false;

public Handler(Callback callback, boolean async) {
    // 如果我们实现的 Handler 是匿名类、成员类、局部类并且不是静态类,系统会提示我们可能会产生内存泄露,正常情况下
    // 我们使用 Android Studio 编写出前面 SendMessage 方式的那种 Handler 的时候,就会亮黄线给予警告。
    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());
        }
    }

    // 初始化 Looper
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    // 初始化 MessageQueue
    mQueue = mLooper.mQueue;
    // 赋值包含 handleMessage()方法的接口的成员变量一个实现类对象
    mCallback = callback;
    mAsynchronous = async;
}

在 Looper 中是如何提供给 Handler Looper 对象和 MessageQueue对象的呢?

 // sThreadLocal.get() will return null unless you've called prepare().
  static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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

/**
  * 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() {
     // 获取 Looper
     return sThreadLocal.get();
 }

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

从上面的代码我们知道了 Looper是如何创建并和当前线程产生关联的,从文档中得知,Looper的主要工作依赖于 loop()方法,那么 loop()方法如何工作呢?

 public static void loop() {
     final Looper me = myLooper();
     final MessageQueue queue = me.mQueue;
     // 轮询消息队列,调用 Handler 的dispatchMessage()分发消息
     for (;;) {
            Message msg = queue.next();
            ...
             try {
                 // 分发消息
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            }

确认一下 Message.target 是否是 Handler

public final class Message implements Parcelable
{
Handler target;
Runnable callback;
}

继续跟踪 dispatchMessage(msg) 方法进行了什么操作

/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    // 从上面我们知道 callback 就是一个 runnbale,那么这里就是对 runnable 进行处理
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            // @return True if no further handling is desired
            // handleMessage()方法返回true 表明没有消息处理了
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

handleCallback(msg) 方法特别简单,就是执行了 Runnable 的 run() 方法。到这里,post(runnable)类型的消息已经被Looper 从 MessageQueue 中拿出来分发给了 Handler 并且已经处理完成。

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

再看看 handleMessage(msg)方法,这就是一个空方法,我们在编写自己的 Handler 对象的时候通常都会实现一个匿名内部类,再在里面实现 handleMessage(msg)

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

到这里,post(Runnable),sendMessage(Message) 两种类型的消息都已经处理完毕了。

我们换个方向,从 sendMessage(Message msg) 方法这里进去看看消息发送是怎样的流程

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

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

继续跟下去,看看 enqueueMessage()方法中都做了什么

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 这里设置了 Message 的 target 是当前 Handler
    // 在后续Looper通过 loop()方法轮询 MessageQueue的时候,就是通过调用Handler 的 dispatchMessage(msg)方法
    // 又将消息处理带回到了 Handler 中来。
    //  msg.target.dispatchMessage(msg);
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

在 enqueueMessage()方法中,将 Message 存储到了 MessageQueue中,等待被Looper 轮询。

  boolean enqueueMessage(Message msg, long when) {
  msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
            ...
            return true;
            }

到此,整个 Handler 流程已经清晰明朗了。简单总结下:

  • 我们创建 Handler 的时候,Handler 就在内部完成了 Looper 和 MessageQueue 的创建工作和初始化
  • Handler 发送消息就是将消息放入了 MessageQueue 中,同时设置 target 为Handler
  • Looper 则通过loop()方法轮询消息,然后通过 前面的 target 方法调用 Handler 的 dispatchMessage 方法分发消息
  • 在dispatchMessage 方法中,如果是 Runnable类型的消息就直接 run(),如果是 Message 类型的消息,就调用我们自定义的 handleMessage() 方法

UI 线程和 Handler

在 Android 应用启动时,会默认有一个主线程(UI线程) , 在这个线程中会关联一个消息队列,所有的操作都会被封装成消息然后交给主线程处理。为了保证主线程不退出,会将获取消息的操作放进一个死循环中,这样主线程就相当于一直在执行死循环,因此不会退出。

UI 线程的消息循环是在 ActivityThread.main() 方法中创建的,源码如下,略有删减:

public static void main(String[] args) {
...
    // 创建消息循环的 Looper
    Looper.prepareMainLooper();

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

    if (sMainThreadHandler == null) {
        // UI 线程的 Handler
        sMainThreadHandler = thread.getHandler();
    }
     // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    // 执行消息循环
    Looper.loop();

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

执行完 ActivityThread.main()方法以后,程序就启动了并且会一直从消息队列中取消息,然后处理消息,使得系统运转起来,UI 线程中,将消息投递到消息队列和从消息队列中获取消息处理的依然是 Handler。

从前面的源码分析中我们知道,每一个 Handler 都有一个消息队列MessageQueue,而消息队列是封装在 Looper 中的,而Looper 又会关联一个线程(通过 ThreadLocal封装),最终就等于一个 Handler 关联一个线程。

默认情况下,消息队列只有一个,就是主线程消息队列,这个是在 ActivityThread.main()方法中通过 Looper.prepareMainLooper() 创建的(最终会调用 Looper()构造方法,里面有 mQueue = new MessageQueue(quitAllowed);)。这样,消息队列就和线程关联上了,所以不同线程,不能访问对方的消息队列。

所以说,为什么我们要更新UI,必须在 UI 线程创建 Handler ,因为 Handler 关联了消息队列,消息队列是在Looper 中创建,Looper 是关联具体的线程的。要使HandleMessage方法可以更新UI,就必须在UI线程创建 Handler ,才能访问到UI线程的消息队列和Looper,才能正常的发送消息和处理消息。

子线程中创建 Handler 为何会抛出异常

如果我们在子线程中创建Handler 那么会遇到下面代码注释中的问题,为什么呢?

    public void show() {
        new Thread(new Runnable() {
            Handler handler = null;
            @Override
            public void run() {
               // Looper.prepare();
                //  "Can't create handler inside thread that has not called Looper.prepare()
               handler = new Handler();
               // Looper.loop();
            }
        }).start();
    }

通过前面的分析我们知道,Handler 在创建的时候,会关联一个消息队列,用于给消息队发送消息,消息队列又是封装在 Looper 中的,所以我们需要一个 Looper,Looper 获取是通过 Looper.myLooper()方法获取的,这个方法的内部是 ThreadLocal.get(),那么这个Looper 是什么时候设置进 ThreadLocal 的呢?就是在 Looper.prepare() 方法中,在这里构建了一个新的Looper,创建了Looper 的消息队列,然后设置到了 ThreadLocal 中。

所以,我们要在子线程中正常的创建 Handler ,必须要在 创建 Handler 之前,调用 Looper .prepare()方法,准备好Looper才不会报错

如果我们有了Looper ,发送消息就可以了吗?不行,还需要调用 Looper.loop()方法需遍历消息,否则,我们的消息只是发送到消息队列中,没有进行任何处理是没有什么效果的。

为什么我们在主线程中创建 Handler 的时候,不需要写 Looper.prepare()/Looper.loop() 呢?从前面的分析我们已经知道了,这两个步骤已经在程序启动的时候,在 ActivityThread.main()方法中做好了,我们需要做的就是创建我们自己的 Handler 绑定上去。

Handler优化

因为如果是直接在主线程创建成员变量 Handler mHandler = new Handler(){…}这样的话,Handler 是持有外部 Activity 的引用的,如果在某一个时候,Activity 被销毁了,但是 Handler 对应的 MessageQueue 中还有待处理的方法,那么就会引起内存泄露,为了避免这个问题,我们可以采用三个步骤:

1、用static声明handler,静态类不会引用外部类
2、如果Handler中必须用到Activity,那就用WeakReference去引用
3、在Activity结束或暂停的事件中,removeMessages或者removeCallbacksAndMessages将消息队列中的消息移除(避免满足上面两条后,当Activity关闭了,但是Handler还未处理到,造成内存泄露)

示例代码:

 private Handler mHandler = new MessageHandler(this);

// 创建一个静态内部类,使用若引用持有外部 Activity
private static final class MessageHandler extends Handler {
    private WeakReference<? extends AppCompatActivity> mReference;

    public MessageHandler(AppCompatActivity activity) {
        super();
        mReference = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        HandlerWithMessageActivity activity = (HandlerWithMessageActivity) mReference.get();
        switch (msg.what) {
            case 1:
                activity.updateTextView("更新 TextView 文字");
                break;

            default:
                break;
        }
    }
}
...
Message message = Message.obtain();
message.what = WHAT_MESSAGE;
mHandler.sendMessage(message);

...
@Override
protected void onPause() {
    super.onPause();
    mHandler.removeCallbacksAndMessages(null);
}

猜你喜欢

转载自blog.csdn.net/xiao6gui/article/details/80817901