欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

写给小白的android Handler机制详解

程序员文章站 2022-03-08 11:53:50
...
  • 当应用启动时会默认有一个UI线程,这个线程会默认关联一个消息队列,所有的操作都会被封装为消息然后交给主线程处理。
  • 在一个线程内创建Handler对象之前必须先调用Looper.prepare
  • handler的无参构造函数,会调用handler两个参数的构造函数,从而调用Looper的静态方法myLooper()获取looper对象,如果looper对象为空,就会报错
  • 每个Looper对象持有一个MessageQueue的引用
public Handler(Callback callback, boolean async) {

       ...

        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;
    }
  • myLooper()会从sThreadLocal.get()获取Looper对象,所以在获取之前必须存入Looper对象,在源码中搜索可以发现,Looper对象在Looper.prepare函数中被存入ThreadLocal中的。
public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

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

上面的代码可以看到,prepare()先判断mLooper是否为空,如果为空的话就创建一个Looper对象,然后保存到ThreadLocal对象内。

总结:

Handler()

↓ Handler构造函数中通过Looper.myLooper()获取Looper对象

Looper.myLooper()

↓ myLooper()通过sThreadLocal.get()获取Looper对象

ThreadLocal.get()

↑ Looper.prepare()创建Looper对象保存到sThreadLocal中

Looper.prepare()

handler.sendMessager()

sendMeaasger()会调用sendMessageDelayed(),如果时间小于零就赋值为零,然后调用sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

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

在sendMessageAtTime()中先获取Looper对象的MessageQueue对象,然后调用 enqueueMessage()

  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()首先将meg.target设置为当前对象,然后调用queue.enqueueMessage()

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

queue.enqueueMessage()通过每个消息的时间将消息进行排列,用mMessage来存储当前第一个待处理的Messager,以链表的形式存储消息。现在我们已经将消息存放到消息队列了。我们知道Looper会从消息队列中取出消息然后发送给Handler处理,接下来我们看Looper实现消息分发的代码

 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;

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

        for (;;) {

            ...

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
    // --------------------------------------------        
                msg.target.dispatchMessage(msg);
    // --------------------------------------------              
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
        ...
        }
    }

直接看标记重点的代码,我们知道msg.target就是当前Handler实例,所以这里就是调用了Handler.dispatchMessage(msg)

以上就实现了Handler发送Message到MessageQueue,然后Looper.loop()调用msg.target(Handler).dispatchMessage()的过程

总结:

Handler.sendMessage()

↓ 调用sendMessageDelayed()对小于0的时间进行正规化

sendMessageDelayed()

↓ 调用sendMessageAtTime()

sendMessageAtTime()

↓ 获取Looper中的MessageQueue 然后调用enqueueMessage()

enqueueMessage()

↓ 调用queue.enqueueMessage()将消息进行入列

queue.enqueueMessage()

Handler.dispatchMessage()

↑ 通过loop()实现从消息队列中取出消息然后将调用msg.target.dispatch

Looper.loop()

Handler.post

handler.post(Runnable),在post中首先会调用getPostMessage将Runnable对象转换成Message对象

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

在post方法里面调用sendMessageDelayed()然后流程又回到了最上面

Handler.post()

↓ 在post中调用getPostMessage(Runnable r)将Runnable转换成Message,然后调用sendMessageDelayed()

sendMessageDelayed()

↓ 调用sendMessageAtTime()

sendMessageAtTime()

↓ 获取Looper中的MessageQueue 然后调用enqueueMessage()

enqueueMessage()

↓ 调用queue.enqueueMessage()将消息进行入列

queue.enqueueMessage()

Handler.dispatchMessage()

↑ 通过loop()实现从消息队列中取出消息然后将调用msg.target.dispatch

Looper.loop()

Handler.dispatchMessage

先检查Message.callback是否为空,如果不为空就执行runnable,如果为空就执行Handler.handlerMessage();

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

Activity的runOnUiThread()

runOnUiThread()也可以实现在子线程内更新UI的逻辑,首先它会判断当前所在线程是否是主线程,如果不是就调用handler.post,然后流程和上面一样,如果在主线程就直接调用run()
    public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }