Handler机制详细解析

Handler机制详细解析简述 1 Android 机制中有个限制 子线程是不能够访问 UI 的 否则会报错

     简述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的退出。

 

 

 

 

 

今天的文章 Handler机制详细解析分享到此就结束了,感谢您的阅读。
编程小号
上一篇 2025-01-07 11:21
下一篇 2025-01-07 11:17

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/104432.html