美文网首页
消息机制--handler

消息机制--handler

作者: 瞬息之李 | 来源:发表于2018-03-09 10:23 被阅读0次

    ThreadLocal: 用来存储不同线程中的数据,在安卓消息机制中,threadloacl用来存储每个线程的looper

    looper:消息循环机制,通过Loop.prepare()方法去创建
    新建的looper将持有一下几个变量

    private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class
    final MessageQueue mQueue;
    final Thread mThread;
    

    新建looper的过程:

    public static void prepare(){
        prepare(true);
    }
    
    public 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));
    }
    
    
    private Looper(boolean quitAllowed) {
         //初始化消息队列
        mQueue = new MessageQueue(quitAllowed);
        //取得当线线程
        mThread = Thread.currentThread();
    }
    

    这里着重介绍ThreadLocal中的get()和set()两个方法如下:
    存取原则如下:通过线程来映射对应的value,value中维护了一个数组,数组中存放localThread当前对象及对应的数据,存储规则如下:ThreadLocal的值在table数组中的存储位置总是为ThreadLocal的reference字段所标识的对象的下一个位置

    public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }
    
        return (T) values.getAfterMiss(this);
    }
    
    public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);   
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }
    
    void put(ThreadLocal<?> key, Object value) {  
        cleanUp();  
      
        // Keep track of first tombstone. That's where we want to go back  
        // and add an entry if necessary.  
        int firstTombstone = -1;  
      
        for (int index = key.hash & mask;; index = next(index)) {  
            Object k = table[index];  
      
            if (k == key.reference) {  
                // Replace existing entry.  
                table[index + 1] = value;  
                return;  
            }  
      
            if (k == null) {  
                if (firstTombstone == -1) {  
                    // Fill in null slot.  
                    table[index] = key.reference;  
                    table[index + 1] = value;  
                    size++;  
                    return;  
                }  
      
                // Go back and replace first tombstone.  
                table[firstTombstone] = key.reference;  
                table[firstTombstone + 1] = value;  
                tombstones--;  
                size++;  
                return;  
            }  
      
            // Remember first tombstone.  
            if (firstTombstone == -1 && k == TOMBSTONE) {  
                firstTombstone = index;  
            }  
        }  
    }  
    
    
    Values values(Thread current) {   
      return current.localValues;
    }
    

    handler:
    这里先列举下handle的几个构造方法:在新建一个handler对象的时候,其实已经关联了对应线程的looper对象(1.当handle没有动态去设置looper时,根据创建handler的线程,取出looper对象,所以用最终处理消息对应的线程就是创建handler的线程。2.当手动去传对应的looper对象时,最终处理消息的线程是根据looper所在的线程决定的,handle只是looper在执行中的一个对象而已,处理对象是不分线程的),取出了looper的msgqueue

    //可以传自定义的looper,callback
      public Handler(Looper looper, Callback callback, boolean async) {
            mLooper = looper;
            mQueue = looper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    
    //平时没有传任何参数时,最终会走到这个方法,参数:null,false
    //callback:用来在最后处理消息的时候调用,下文给出
    public Handler(Callback callback, boolean async) {
            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());
                }
            }
    
            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;
        }
    

    使用handler去发送消息的过程:

       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);  
    }  
      
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
           msg.target = this;  
           if (mAsynchronous) {  
               msg.setAsynchronous(true);  
           }  
           //这里msg被加入消息队列queue  
           return queue.enqueueMessage(msg, uptimeMillis);  
    

    接下来讲解looper如何从msgqueue中取出消息进行处理:
    在新建handler对象后,looper要调用loop()方法,不断从msgqueue中取出消息处理,代码如下

    public static void loop() {  
         final Looper me = myLooper();  
         if (me == null) {  
             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
         }  
         //从Looper中取出消息队列  
         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 (;;) {  
             Message msg = queue.next(); // might block 这里会被阻塞,如果没有新消息  
             if (msg == null) {  
                 // No message indicates that the message queue is quitting.  
                 return;  
             }  
      
             // This must be in a local variable, in case a UI event sets the logger  
             Printer logging = me.mLogging;  
             if (logging != null) {  
                 logging.println(">>>>> Dispatching to " + msg.target + " " +  
                         msg.callback + ": " + msg.what);  
             }  
      
             //将消息交给target处理,这个target就是Handler类型  
             msg.target.dispatchMessage(msg);  
      
             if (logging != null) {  
                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);  
             }  
      
             // Make sure that during the course of dispatching the  
             // identity of the thread wasn't corrupted.  
             final long newIdent = Binder.clearCallingIdentity();  
             if (ident != newIdent) {  
                 Log.wtf(TAG, "Thread identity changed from 0x"  
                         + Long.toHexString(ident) + " to 0x"  
                         + Long.toHexString(newIdent) + " while dispatching to "  
                         + msg.target.getClass().getName() + " "  
                         + msg.callback + " what=" + msg.what);  
             }  
      
             msg.recycle();  
         }  
     }  
    

    从上面loop()方法中,当有msg时,将会执行msg.target.dispatchMessage(msg) 方法,其中msg.target就是handler,这个在sendMseeage时,对msg进行了处理,由此可得最后由handler的dispatchMessage(msg)方法得到执行:

    public void dispatchMessage(Message msg) {  
        if (msg.callback != null) {  
            //这个方法很简单,直接调用msg.callback.run();  
            handleCallback(msg);  
        } else {  
            //如果我们设置了callback会由callback来处理消息  
            if (mCallback != null) {  
                if (mCallback.handleMessage(msg)) {  
                    return;  
                }  
            }  
            //否则消息就由这里来处理,这是我们最常用的处理方式  
            handleMessage(msg);  
        }  
    }  
    

    其中msg.callback是个Runnable接口的实现,什么时候会设置这个callback:handler.post(runnable),post方法最终会被封装成msg对象传过去

    public final boolean post(Runnable run){
      return sendMessageDelay(getPostMessage(run),0);
    }
    

    mCallback :这个是在新建handler对象的时候传进去的,当没有传时为null
    最后才会执行handler的handleMessage(msg)方法

    注意事项:

    1. 在主线程创建handle时,系统默认提供了mainLooper,所以不需要再自己去创建looper了。但是在子线程时,需要自己去创建looper,并要去手动执行loop()方法去轮询查找消息,
    2. msg在哪个线程执行是依赖handler创建时的线程?还是looper所在的线程?--- 答案是looper所在的线程,因为在处理消息时,最终是由looper的loop()方法调用,再去调用handler对应的handleMessage()方法。
      这里注意一点,对象是不分线程的

    这里举几个典型的例子:

    子线程想要在主线程中执行一段代码
    new Handler(getMainLooper()).post(new Runnable() {
    
            @Override
    
            public void run() {
    
                //todo
    
            }
    
        })
    

    相关文章

      网友评论

          本文标题:消息机制--handler

          本文链接:https://www.haomeiwen.com/subject/dazbnxtx.html