malinkang


人生最可悲的事情,莫过于胸怀大志,却又虚度光阴。


Handler的使用和原理分析

目录

使用Handler

每个应用程序都有自己的特殊线程来运行UI对象,例如View对象;这个线程称为UI线程。在Android中,不允许非UI线程访问和修改UI对象。但是在实际的开发中,很多地方要在非UI线程中修改UI对象。比如下载网络图片并将图片设置给ImageView

Handler允许您发送和处理与线程的MessageQueue关联的Message和Runnable对象。每个Handler实例都与一个线程和该线程的消息队列相关联。当您创建一个新的Handler时,它被绑定到正在创建它的线程的线程/消息队列 - 从那时起,它将消息和runnables传递给该消息队列并在消息出来时执行它们队列。将Handler连接到UI线程时,处理消息的代码在UI线程上运行。

Handler有两个主要用途:

  1. 发送消息;
  2. 在不同于发送的线程上处理消息。

发送消息通过以下方法来完成:

  • post(Runnable)
  • postAtTime(java.lang.Runnable, long)
  • postDelayed(Runnable, Object, long)
  • sendEmptyMessage(int)
  • sendMessage(Message)
  • sendMessageAtTime(Message, long)
  • sendMessageDelayed(Message, long)

post版本允许您将Runnable对象排入队列,以便在收到它们时由消息队列调用;sendMessage版本允许您将包含将由Handler的handleMessage(Message)方法处理的数据包的Message对象排入队列。

原理解析

Handler

在创建Handler的时候会获取当前线程的Looper对象。HandlerLooper在同一个线程。

public Handler(Callback callback, boolean async) {
    //...
    //获取Looper对象
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    //获取Looper对象的MessageQueue对象
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

Looper对象的myLooper方法

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

ThreadLocal即线程本地存储区(Thread Local Storage,简称为TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。这里线程自己的本地存储区域存放是线程自己的Looper。

Handler发送消息在另外一个线程。发送消息线程和Lopper所在的线程共享MessageQueue对象。发送消息会将Message对象放入到MessageQueue中。

//所有的send方法都会走到sendMessageAtTime方法
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;
    }
    //将消息压入到MessageQueue中
    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);
}
public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        //
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

Looper

Looper可以在主线程中使用,也可以在普通线程中使用。

Looper使用包括三个步骤:

  • 调用prepare创建Looper对象
  • 创建处理消息的handler
  • Looper调用loop()方法循环的取消息

ActivityThreadmain函数中,系统为主线程创建了Looper对象。

//ActivityThread的main方法
public static void main(String[] args) {
    //在主线程中调用prepareMainLooper方法,普通线程中调用prepare方法
    Looper.prepareMainLooper(); 

    // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
    // It will be in the format "seq=114"
    long startSeq = 0;
    if (args != null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                startSeq = Long.parseLong(
                        args[i].substring(PROC_START_SEQ_IDENT.length()));
            }
        }
    }
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

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

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();

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

Looper的prepareMainLooper方法会创建一个Looper实例然后放入到ThreadLocal中,然后创建Handler时会获取该实例。

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

创建Looper的时候,会创建一个MessageQueue对象。

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

Looperloop()方法中循环从MessageQueue中取出Message,然后通过Handler来处理消息。假若队列为空,那么它会进入休眠。

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;
        }
        //...
        try {
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        //...
        msg.recycleUnchecked();

    }
}

MessageQueue

MessageQueue是一个消息队列,具有“队列”的所有常规操作。包括:

  • 新建队列
  • 元素入队
  • 删除元素
  • 销毁队列

参考

最近的文章

flutter-json

于  继续阅读
更早的文章

《Effective Java》读书笔记 第8章 通用程序设计

第45条:将局部变量的作用域最小化要使局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方声明。 循环中提供了特殊的机会来将变量的作用域最小化。for循环,都允许声明循环变量(loop variable),它们的作用域被限定在正好需要的范围之内。因此,如果在循环终止之后不再需要循环变量的内容 …

于  , 继续阅读
comments powered by Disqus