Handler机制详细解析

     简述1: Android机制中有个限制,子线程是不能够访问UI的,否则会报错。而主线程中不允许执行耗时操作,因为如果在一定的时间没有处理完,就会阻塞主线程而出现ANR异常。所以一些耗时的任务,比如IO流读写,网络请求等操作就需要创建一个子线程中去处理,任务结束后如果涉及到UI的更新操作,就必需再切换到主线程中去做后续的处理。而这个线程的切换就用到了Handle这么一个机制。

简述2:Hander机制的四个重要成员:Looper   Handler  Message  MessageQueue。大致流程就是 :

        1.先在在当前线程中,创建一个Looper对象,此时在其内部也会创建一个MessageQueue,并把这个Looper对象保存到当前线程的sThreadLocal中。

        2.在当前线程中,创建一个Hander对象并重写handMessage()方法,创建Handler对象的过程中会从sThreadLocal取到当前线程的looper对象以及其中的MessageQueue,这样handler对象中就有这个MessageQueue了。

        3.Looper.loop(),进行轮询即是不停的取Looper内部的MessageQueue中的消息,如果消息队列中没有消息则会阻塞。

        4.当在某个其他的线程中调用handler.handeMessage(msg)发送一个消息的时候,这个msg就会把这个handler包装到msg中,并将这个msg添加到这个MessageQueue中。此时创建此handler对象的线程中,对应的Looper就会轮询到这个msg,并调用这个handler的handleMessage()方法去处理对应的逻辑。

        5.由于这个looper的轮询是在创建这个looper对象的线程中,所以就实现了线程的切换。

 注意:

           1.handler发送的消息,做所以能被对应的looper轮询到,是因为他们都拥有同一个MessageQueue。

            2.之所以不会出现不同线程的handler和looper的错乱,是因为Looper的存取是通过sThreadLocal进行的。Handler和Looper基于同一个线程创建的,并共有同一个消息队列。

       

上面叙述的流程其实也包含了一些原理,下面我们来分析一下源码去更好更全面的理解这些步骤:

  1.首先Looper的创建是通过调用Looper.prepare();方法:

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

//初始化Looper,并把这个对象存储到sThreadLocal这个对象中去。
private static void prepare(boolean quitAllowed) {
//quitAllowed 代表是否允许Looper的退出。 一般都允许,主线程除外。
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

//下面代码是创建Looper对象,此时构造参数中会创建mQueue消息队列,并得到当前的线程mThread。

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

ThreadLocal知识点:这个类是用来存储各种泛型的数据对象,和其他存储工具类不一样的是,同一个mThreadLocal对象在不同的线程中进行数据的存取操作是相互独立的,即是每个线程都有这个mThreadLocal副本,他们各存各的,各用各的,互不干扰。Looper类中的sThreadLocal这个对象是静态全局的变量,他可以在各个线程中对创建的Looper对象进行存储和获取。他互不干扰的原因就是他存取使用的key是当前的线程,即实现了Looper和Threader的一一对应。下面看看源码吧:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

//存储数据:获取当前的线程,然后调用getMap(t)方法获取ThreadLocalMap对象
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

//然后根据当前线程 t  取出这个线程t的ThreaLocaMap对象,然后调用 set方法进行存储

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
private void set(ThreadLocal<?> key, Object value) {
  
    Entry[] tab = table;                       //创建一个数组
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);//根据当前的sThreadLocal,通过算法计算出一个i即是index角标
    tab[i] = new Entry(key, value);         //然后将此线程中的sThreadLocal和Looper封装成Entry并赋值给table数组的index
    int sz = ++size;                         //到此及完成了存储
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}

如果是第一次用,getMap为空,需要重新创建一个ThreadLocalMap对象,并赋值给这个r的threadLocals变量,在new对象的过程中完成存储,和上面一样的。

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}

思路:获取当前线程 --->  然后找这个线程对应的ThreadLocalMap对象 -----> 在对象获取数组table---->将value封装成生成Entry并存储到table[i]里。    

取数据和存数据原理一样就是逆着来:所以同一个sThreadLocal对象,在不同线程中取到的只是该线程下的looper对象,不会出现错乱,looper不错乱了

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

2.Handler的创建

情况一:

Handler  handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};  

源码:
public Handler() {
    this(null, false);
}

//这个过程就将looper和handler绑在了一条线程上,并共用一个messageQueue消息队列。由于一个线程的looper是确定的,所以这个线程的handler也是确定的。

public Handler(Callback callback, boolean async) {
  
    mLooper = Looper.myLooper();//获取当前线程的Looper
    if (mLooper == null) {   //所以在此线程中创建handler之前一定确保此线程中已经创建looper对象
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

情况二:实现了Handler.Callback这个接口中的handleMessage()方法

Handler handler=new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        return false;
    }
});

情况三:此时handler所在的线程即是Looper.myLooper()所在的线程。

Handler handler=new Handler(Looper.myLooper(), new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        return false;
    }
}) 
 或 
Handler handler=new Handler(Looper.getMainLooper()){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};

3.发送消息:

  情况一:send发送

handler.sendEmptyMessage(100);

情况二:其实也是把Runnable打包到Message中去了,即是Message.callback==Message.runnable,然后再send发送

handler.post(new Runnable() {
    @Override
    public void 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;
}

所以我们接着看send方法:

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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

//MessageQueue消息队列添加发送过来的消息,如果之前处于阻塞状态,则会激活。否则就直接往里面添加消息。

//MessageQueue 是通过单链表的结构来维护消息的,效率高。

boolean enqueueMessage(Message msg, long when) {
   .......
    synchronized (this) {
        if (mQuitting) {//如果looper调用了quie ,消息队列也调用quit(),此时mQuitting=true消息队列退出并释放。
            msg.recycle();
            return false;
        }

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // 消息队列为空的时候,指针指头
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
           
            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;
        }
//如果之前MessageQueue是处于阻塞状态,当有新消息插入的时候,会唤醒MessageQueue,从而继续轮询读取 Msg和处理
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

4.Looper.poop();轮询读取MessageQueue中的消息,并且进行处理。先进添加的先处理。

public static void loop() {
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
        for (;;) {
        Message msg = queue.next(); // 从当前线程获取looper,并得到messageQueue,然后调用其next()不停的取数据
        if (msg == null) {
            // 如果队列中没有消息,则messageQueue会阻塞,从而导致loop()的阻塞
            return;
        }

     ...............
       //如果有消息就会调用msg 中的handler(即是发送消息的handler)中的dispatchMessage(msg)去处理。
            msg.target.dispatchMessage(msg);
     ..............
}

先看消息队列是咋取消息的:
Message next() {
    .......
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
       //第二个参数代表等待多久又返回。0则无需等到直接返回,如果是-1 则会阻塞。直到重新有消息添加唤醒。
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            .....
            if (msg != null) {
                if (now < msg.when) {
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                //没有消息则为-1,下次循环便会阻塞
                nextPollTimeoutMillis = -1;
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }
       }
}

再看看handler.dispatchMessage()是咋处理消息的:(他是在loop轮询的线程处理的,也就是创建looper,handler的线程,所以便实现了切换线程的目的)

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {//如果是post(Runnable),此时msg.callback==runnable,则执行runnable任务
        handleCallback(msg);
    } else {   
        if (mCallback != null) {//如果创建handler的构造传handle.CallBack,则优先执行其中的handMeaasge(msg)
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);//否则执行 重写的handMessage(msg);
    }
}

Looper的退出:

public void quit() {
    mQueue.quit(false);
}
void quit(boolean safe) {
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }

    synchronized (this) {
        if (mQuitting) {
            return;
        }
        mQuitting = true;

        if (safe) {
            removeAllFutureMessagesLocked();//安全退出,执行完所有的msg,队列退出
        } else {
            removeAllMessagesLocked();//直接退出
        }

        // We can assume mPtr != 0 because mQuitting was previously false.
        nativeWake(mPtr);
    }
}

注意:如果是手动创建的Looper,则任务执行完需要调用quit()退出,否则Looper会一直处于轮询状态,消耗性能。

到此也就算详细的走了一遍流程,包括涉及到的一些原理。可以根据这个流程跟一下主线程是咋回事,当然了主线程也是按照这个原理工作的,只是他比较特殊,有专门的方法去初始化这块机制,另外还不让执行Looper的退出。

猜你喜欢

转载自blog.csdn.net/lk2021991/article/details/88407584