To be continued !!!!!!

Handler

When I was watching the TV play, I suddenly felt something. You can combine the Handler mechanism with the airport carousel (mine is a more advanced carousel that rotates when there is luggage and standby when there is no luggage).

  • Looper.loop() : a conveyor belt machine (power source).
  • MessageQueue: A conveyor belt.
  • Handler: A person who places and picks up luggage on a conveyor belt.
  • Message: compared to a luggage frame (some airports keep luggage from getting dirty).
  • The contents of the Message object: compared to luggage.

For the sake of memory and learning, suppose that the main thread MessageQueue has no Message causing the main thread to block, then an Activity executes mhandler.sendMessage ();

Break the process down into five steps:

  • 1: energize (looper.loop ()), block (nativePollOnce()).
  • 2: Unpack (mhandler.sendMessage () -> nativeWake()).
  • 3: The conveyor belt turns.
  • 4: baggage handler.dispatch ().
  • 5: Using message.recycleunchecked ().

1: electricity

frameworks\base\core\java\android\app\ActivityThread.java

    // 1: main method entry
    public static void main(String[] args) {.../ / 2:
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false); ./ / 8:
        Looper.loop();

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

frameworks\base\core\java\android\os\Looper.java

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();// Unique in process

// The constructor is private. The Looper object can only be created using prepare
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
}

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

/ / 4:
// Ensure that one thread corresponds to one Looper and one Looper corresponds to one MessageQueue.
private static void prepare(boolean quitAllowed) {
        if(sThreadLocal.get() ! =null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        / / 5:
        sThreadLocal.set(new Looper(quitAllowed));
}
/ / 3:
public static void prepareMainLooper(a) {
        prepare(false);
        synchronized (Looper.class) {
            if(sMainLooper ! =null) {
                throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); }}/ / 9:
public static void loop(a) {
        final Looper me = myLooper();// sThreadLocal.get()
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        for (;;) {
            / / 10:
            Message msg = queue.next(); // might block. msg.target.dispatchMessage(msg);// This is the handler memory leak. Target is the handler object}}Copy the code

libcore\ojluni\src\main\java\java\lang\ThreadLocal.java

/ / 6:
 public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map ! =null)
            map.set(this, value); 
        else
            createMap(t, value);
    }

static class ThreadLocalMap {
	/ / 7:
     private void set(ThreadLocal
        key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);// The current threadLocal hash is used as the subscript.
            for(Entry e = tab[i]; e ! =null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<? > k = e.get();if (k == key) {
                    e.value = value;
                    return;
                }
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
      }
}
Copy the code

frameworks\base\core\java\android\os\MessageQueue.java

Message next(a) {...for(;;) {...// 12: blocks when there is no message
            nativePollOnce(ptr, nextPollTimeoutMillis);// Block the operation. }boolean enqueueMessage(Message msg, long when) {... nativeWake(mPtr);return true;
}
Copy the code

2: Unpack

Handler.java

    // 1: sendMessage is finally called
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;/ / the Message Handler.
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        / / 2:
        return queue.enqueueMessage(msg, uptimeMillis);
    }
Copy the code

MessageQueue.java

Message next(a) {...for(;;) {... nativePollOnce(ptr, nextPollTimeoutMillis);// Block the operation. }/ / 3:
boolean enqueueMessage(Message msg, long when) {.../ / 4:
         nativeWake(mPtr); 

        return true;
}
Copy the code

3: The transmission gets going

4: Baggage claim

Looper.java

public static void loop(a) {
        final Looper me = myLooper();// sThreadLocal.get()
        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./ / 1:
            msg.target.dispatchMessage(msg);// This is the handler memory leak. Target is the handler object. msg.recycleUnchecked(); }}Copy the code

Handler.java

    // 2: chain call
    public void dispatchMessage(Message msg) {
        if(msg.callback ! =null) {
            handleCallback(msg);
        } else {
            if(mCallback ! =null) {
                if (mCallback.handleMessage(msg)) {
                    return; } } handleMessage(msg); }}Copy the code

5: Recycle Message

Looper.java

public static void loop(a) {
        final Looper me = myLooper();// sThreadLocal.get()
        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. msg.target.dispatchMessage(msg);// This is the handler memory leak. Target is the handler object./ / 1:msg.recycleUnchecked(); }}Copy the code

Message.java

    / / 2:
    void recycleUnchecked(a) {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;
        
        The capacity of the Message pool is 50. If the capacity exceeds 50, the Message pool is discarded and waits for GC
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {  //MAX_POOL_SIZE = 50  
                next = sPool;
                sPool = this; sPoolSize++; }}}Copy the code

How to ensure that a thread has only one Looper

  • 1: Privatize the Looper constructor
  • 2: creates a Looper object by providing only prepare ()
  • 3: Ensure that the current process has only one sThreadLocal using static final.

A Thread corresponding to a ThreadLocal. ThreadLocalMap ThreadLocal data storage. A Thread corresponds to multiple ThreadLocal. A ThreadLocal can store only one data.

  • 4: sThreadLocal. Set (new stars (quitAllowed)). use

Looper.java

    //3: Static final guarantees that the current process has only one sThreadLocal.
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    //1: Privatize the constructor
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    //2: creates the Looper object by providing only prepare ()
    public static void prepare(a) {
        prepare(true);
    }

    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(a) {
        prepare(false);
        synchronized (Looper.class) {
            if(sMainLooper ! =null) {
                throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); }}Copy the code

ThreadLocal.java

  • The Java standard library provides a special ThreadLocal that can pass the same object in a single thread.
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);/ / finally returned to the Thread. Java ThreadLocal. ThreadLocalMap threadLocals.
        if(map ! =null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    
    static class ThreadLocalMap {
    
        static class Entry extends WeakReference<ThreadLocal<? >>{ Object value; Entry(ThreadLocal<? > k, Object v) {super(k); value = v; }}private Entry[] table;
        
        private void set(ThreadLocal
        key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            for(Entry e = tab[i]; e ! =null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<? > k = e.get();if (k == key) {
                    e.value = value;
                    return;
                }
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
      
    }
Copy the code

thread

Thread switching is essentially a thread switching between executing shared resources.

  • MHandler = new Handler(); Get mHandler. MQueue MessageQueue
  • 2: in the child thread mhandler.sendMessage ();

Handler.java

    // 2: child thread
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue; // Shared MessageQueue
        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);
    }
    
    // 1: main thread Handler instantiated
    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();// Main () ->prepare()
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue; // Shared MessageQueue
        mCallback = callback;
        mAsynchronous = async;
    }    
Copy the code