Android source code analysis--flyweight design pattern, handler message passing mechanism (based on Android API 33 SDK analysis)

Android source code analysis – flyweight design pattern, handler message passing mechanism (based on Android API 33 SDK analysis)

1. Definition

Use shared objects to efficiently support large numbers of fine-grained objects

Core: Object reuse.

1.1 Flyweight Mode Demo

Train ticket purchase demo

//火车票
public class Ticket {
    
    
    private String from;
    private String to;

    public Ticket(String from, String to) {
    
    
        this.from = from;
        this.to = to;
    }

    public int getPrice() {
    
    
        return new Random().nextInt(100) + 20;
    }
}

Cache objects in a Map. Below we will also analyze

//火车票查询工厂
public class TicketFactory {
    
    
    public static Map<String, Ticket> sTicketMap = new HashMap<>();

    public static Ticket getTicket(String from, String to) {
    
    
        String key = from + "-" + to + "";
        Ticket ticket = sTicketMap.get(key);
        if (ticket != null) {
    
    
            return ticket;
        }
        ticket = new Ticket(from, to);
        sTicketMap.put(key, ticket);
        return ticket;
    }
}

2. Analysis of source code examples in Android Message

usage

val obtain = Message.obtain()

Follow along

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
    
    
        //防止多线程并发
        synchronized (sPoolSync) {
    
    
            //从线程池取对象
            if (sPool != null) {
    
    
                //链表取出每个Message对象
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

This is the most obvious flyweight design pattern.

3. Message’s associated Handler

A knowledge point in Android development: UI cannot be updated in child threads.

class DebugActivity : AppCompatActivity() {
    
    
    private val TAG = javaClass.simpleName
    private var handler: Handler = Handler(Looper.getMainLooper())
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
    fun doSomething(){
    
    
        thread {
    
    
            //耗时操作,得到结果,不能在这个线程更新 UI
            // Handler 将结果传递到主线程中,更新UI
            handler.post {
    
    
                //更新UI
            }
        }
    }
}

We follow up the post function

 public final boolean post(@NonNull Runnable r) {
    
    
    
       return  sendMessageDelayed(getPostMessage(r), 0);
 }
 private static Message getPostMessage(Runnable r) {
    
    
        Message m = Message.obtain();
        m.callback = r;
        return m;
  } 

The Handler passes a Runnable to the UI thread and installs it into a Message object.

Follow up the sendMessageDelayed function

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    
    
        if (delayMillis < 0) {
    
    
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
 }
 public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    
    
     //当前 Handler 所在的消息队列
        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);
 }

The sendMessageDelayed function calls the sendMessageAtTime function. Without manually passing the Looper, the Looper held by the Handler is the Looper of the current thread. That is to say, the Handler is created in which thread, it is the Looper of that thread.

The Message object in getPostMessage is the Message.obtain() function

 Message m = Message.obtain();

Analyze this code,

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
    
    
        //防止多线程并发
        synchronized (sPoolSync) {
    
    
            //从线程池取对象
            if (sPool != null) {
    
    
                //链表取出每个Message对象
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

The Message message pool does not use a container such as map, but uses a linked list.

Insert image description here

How to put it in this message pool?

Let's see

The Message object is recycled into the message pool

public void recycle() {
    
    
    //该消息还在使用
        if (isInUse()) {
    
    
            if (gCheckRecycle) {
    
    
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
    //消息添加到消息池中
        recycleUnchecked();
    }

Follow up with recycleUnchecked()

 void recycleUnchecked() {
    
    
        // 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 = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;
		//回收消息到消息池中
        synchronized (sPoolSync) {
    
    
            if (sPoolSize < MAX_POOL_SIZE) {
    
    
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

Here the linked list is used as a buffer pool to store message objects. Each time a message is generated, it is added to the linked list.

4. Android message mechanism

The entrance to the Android application is actually ActivityThread, follow it

public static void main(String[] args) {
    
    
        ......
		//创建Looper,UI线程的消息队列
        Looper.prepareMainLooper();
		......
    	//启动应用程序
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
		//循环消息
        Looper.loop();

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

Looper fetches messages from the message queue and processes the messages. The Handler continuously adds messages to the message queue, and the messages are continuously processed.

So how does the Handler associate with the message queue?

Constructor of Handler

public Handler(@Nullable Callback callback, boolean async) {
    
    
      ......
        mLooper = Looper.myLooper();//获取 Looper
 	 ......
        mQueue = mLooper.mQueue;//获取消息队列
        mCallback = callback;
        mAsynchronous = async;
    }

Handler obtains the Looper object through myLooper(),

Follow up with myLooper()

public static @Nullable Looper myLooper() {
    
    
    //myLooper通过sThreadLocal.get()获取
        return sThreadLocal.get();
    }

The Looper object is stored in sThreadLocal,

	@Deprecated
    public static void prepareMainLooper() {
    
    
        prepare(false);
        synchronized (Looper.class) {
    
    
            if (sMainLooper != null) {
    
    
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
//prepare()方法中创建了一个 Looper 对象
	private static void prepare(boolean quitAllowed) {
    
    
        if (sThreadLocal.get() != null) {
    
    
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //将该对象设置给了sThreadLocal,这样线程和队列就关联上了
        sThreadLocal.set(new Looper(quitAllowed));
    }

The Handler is associated with the thread and the thread's message queue, and the messages sent by the Handler will be executed on this thread.

Call the Looper's loop function to continuously retrieve and process messages from the message queue.

 public static void loop() {
    
    
      ......
          //死循环
        for (;;) {
    
    
            //取消息
            if (!loopOnce(me, ident, thresholdOverride)) {
    
    
                return;
            }
        }
    }

Follow up with loopOnce

private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
    
    
    //获取消息 (might block )
        Message msg = me.mQueue.next(); // might block
       ......
        try {
    
    
            //处理消息
            msg.target.dispatchMessage(msg);
            if (observer != null) {
    
    
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } 
    	......
            //回收消息,也就是我们分析享元模式时提到的将 Message 添加到消息池的操作
        msg.recycleUnchecked();

        return true;
    }

Take a look at the core code of next()

Message next() {
    
    
       		......
			//native层的事件
            nativePollOnce(ptr, nextPollTimeoutMillis);
				......
                if (msg != null) {
    
    
                    //消息延迟,
                    if (now < msg.when) {
    
    
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        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 {
    
    
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

              ......
        }
    }

The next function takes out messages from the message queue in sequence. If the message reaches the execution time, then the message is returned to the Looper, and the pointer of the queue list is moved backward.

5. Creating a Handler in a child thread throws an exception

class DebugActivity : AppCompatActivity() {
    
    
    private val TAG = javaClass.simpleName
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        thread {
    
    
            val handler = Handler()
        }
    }
 
}

Insert image description here

Analysis: The Looper object is ThreadLocal, and each thread has its own Looper. When creating a Handler object in a child thread, if the Looper is empty, an exception will be thrown. Follow up on the Handler's construction method

public Handler(@Nullable Callback callback, boolean async) {
    
    
        ......
        //获取looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
    
    
            //抛出异常
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
     ......
    }

The mLooper object is empty and an exception is thrown. The Looper object in this thread has not been created. The Looper object of this thread is empty before Looper.prepare is manually called in the child thread. The solution is to set the Looper object for the current thread before constructing the Handler.

class DebugActivity : AppCompatActivity() {
    
    
    private val TAG = javaClass.simpleName
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        thread {
    
    
            //绑定到 ThreadLocal中
            Looper.prepare()
            val handler = Handler()
            //启动消息循环
            Looper.loop()
        }
    }

}

这样子线程的Looper对象就不会为null了,有了自己的消息队列。

Guess you like

Origin blog.csdn.net/weixin_46039528/article/details/132391529