Handler detailed explanation of Android interview notes

Detailed explanation of Handler for Android interview

Handler eight questions

1: How many Handlers does a thread have?

可以有多个Handler 发送消息

2: How many Loopers are there in a thread? How to guarantee?

2.1:
只能有一个,在创建Handler的时候指定Looper,该Looper创建的线程,就是处理消息的线程
2.2:如何保证?
每一个线程 都有一个ThreadLocal ( 运用了HashMap),用来保存 线程的 状态,标志位等 上下文环境 (大量的key-value 键值对)

如何保证一个key,只有一个value? 
Hash算法  --Hashcode
并且不可修改?
	在setLooper之前会检查,对应key的值是否为空,如果不为空,则抛出异常,保证唯一性

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
Looper.prepare(); -- sThreadLocal.set(new Looper(quitAllowed));  -- map.set(ThreadLocal, value);  
因为线程只有 ThreadLocal,所以保证了只能有一个Looper,也保证了<sThreadLocal, Looper>  两者一一对应,又唯一

sThreadLocal 是static final 修饰的,整个app中只有一个,只会初始化一次 

2.3:Looper和MessageQueue 什么关系?

MessageQueue是在Looper里进行初始化的,并且是Final修饰的,一旦初始化不可改变  两只一一对应



Thread -- ThreadLocalMap -- <sThreadLocal, Looper> -- Looper --MessageMessage
==>一个Thread 只有一个MessageQueue
==>一个Thread 只有一个Looper 

3: The reason for the Handler memory leak? Why haven't other internal classes mentioned this problem?

3.1: What is the cause of Handler memory leak?

	内部类持有外部类对象, 为什么Handler会持有外部类对象?
	handlerMessage方法中,默认持有MainActivity.this对象

	Handler  -- this(Activity)

	在Message.enqueueMessage的时候,msg.target = handler:
	Msg == > 持有 Handler 对象  ==> 持有Activity 对象

	eg:如果这个消息一个小时候后执行,那么msg 会一直持有Activity对象,不能被JVM回收

3.2 How to solve the leakage problem?

	使用static修饰Handler,软引用 弱引用
3.2.1 What does the static keyword do? Why can the leakage problem be solved?
	静态内部类,是不会持有外部类对象的

3.3: Why haven't other inner classes mentioned this problem?

	因为其他内部类不会持有Activity对象

4: Why can the main thread be a new Handler? If you want a new Handler in the sub-thread, what preparations should be made?

4.1 Why can the main thread be a new Handler?

因为 ActivityThread当中已经对Handler的Looper进行了 prepare 和 loop方法的调用

4.2 If you want to prepare the new Handler in the child thread?

	需要 进行Looper.prepare 以及 loop的调用,并且在初始化Handler的时候,将Looper传入

	不过不建议在子线程中进行new Handler,建议使用HandlerThread,已经封装好了

	4.2.1 锁

	wait and notifyAll:wait--释放锁,并且等待被唤醒 notifyALl--通知之前wait的线程就绪,但不会释放锁,等synchronized代码块执行完,进行释放

	内置锁(系统内置的锁):执行完synchronized代码块,由JVM来解锁
	synchronized(object)
	synchronized(this) 
	eg1:
	HandlerThread1中两个函数func1,func2
	handlerThread1 = new HandlerThread1

	两个线程中,分别执行handlerThread1.func1 和 handlerThread1.func2

	结论:当线程1执行synchronized(this) 代码块时,线程2 等待

	func1() {
		synchronized(this) 
		{

			///
		}

	}

	func2() {
		synchronized(this) 
		{

			///
		}

	}

	eg2:同上,将两个函数中的synchronized(this) 替换成 synchronized(object)
	结论:如果两个线程锁的是同一个object,则相互关联,否则,不影响执行顺序 

5: Looper maintained in the child thread, what is the processing plan when there is no message in the message queue? What is the use? What about the main thread?

子线程中消息队列无消息时,睡眠等待,如没有消息,可调用Looper.quit,清空数据
主线程中不允许调用looper.quit函数,为什么?
因为主线程中大量用到了Handler消息处理,比如Activity的启动,UI交互,通信

5.1 MessageQueue source code analysis

	数据结构:链表构成的 优先级队列

	1.入队的时候,根据执行时间排序,队列满的时候,阻塞,直到用户通过next取消息,通知MessageQueue可以进行入队

	2.出队的时候,Looper.loop启动轮询机制,当MessageQueue为空时,队列阻塞,等消息队列调用enqueueMessage时,通知队列可以取出消息,停止阻塞

6: Since there can be multiple Handlers to add data to the MessageQueue (each Handler may be in a different thread when sending a message), how does it ensure thread safety internally?

6.1 因为一个线程中MessageQueue只有一个,在en'queueMessage中运用了锁:synchronized(this),保证每次只能往队列中放一个,保证了线程安全

6.2 next取消息时,为什么也要用到锁?不是会降低取消息速度吗?
因为要保证,在取消时,没有其他消息插入,取消息跟插入消息不能同时进行

6.3 性能优化--线程
线程并不是越多越好,一般最大是 cpu数量的2倍 + 1

Glide Okhttp都有自己的线程池?如何优化?--公用同一个线程池(使用反射机制,重新对框架的线程池赋值)

7: How should we create it when we use Message?

使用obtain,复用--用到了享元设计模式

最大不要超过50个消息

如果不使用obtain,一直new message,会造成 内存抖动==》GC == 》 卡顿

在自定义View中Draw(),也不能使用new,也会造成同样的内存问题

8: Why does the infinite loop of Looper not cause the application to freeze

8.1 Since the Handler message interprets Loop, why is there no ANR problem?

之前不是说五秒钟不响应就会出现ANR问题吗?为什么休眠好长时间也不会ANR?

产生ANR的原因不是因为主线程休眠时,而是因为输入事件没有响应,输入事件没有响应就没法唤醒Looper,才加入了五秒限制

8.2 The application is sleeping when there is no message, and releasing the thread will not cause the application to be stuck. The stuck is ANR, and Looper is sleeping

Thinking? Conveyor mode

1. How does Handler realize the leap between threads?

子线程发送消息,主线程取出消息

子线程:handler.sendMessage-- enqueueMessage--  queue.enqueueMessage(管理内存的)

主线程:ActivityThread 中调用 Looper.loop -- queue.next 取出消息 -- dispatchMessage分发消息 --handler.handlerMessage

Insert picture description here

2.Activity startup process

Launcher 应用里点击 我们的应用图片,启动  zygote,创建一个JVM,JVM会去调用ActivityThread中的main函数

2.1 ActivityThread starts Activity

Insert picture description here

2.2 Root Activity startup sequence diagram

Insert picture description here

3. Handler communication mechanism is the mechanism for managing memory in threads

4. Synchronization barrier of message mechanism

4.1 If there is an event (message) that must be executed as soon as possible, how does the Handler ensure the smooth execution of the event through the message barrier?

4.2 The messages are sorted according to the execution time, and then the messages are stored in the queue, so the messages can only be taken out from the head of the queue. So here comes the question! What should I do with messages that need to be dealt with urgently?

其实是通过设置 同步屏障来解决的

4.3 So what is the synchronization barrier?

4.3.1 First, let me explain, Message in Handler is divided into synchronous message and asynchronous message. Generally speaking, there is no difference between the two kinds of messages. The difference will only occur when the synchronization barrier is set.
4.3.2 Generally Messages will have targets, but there is no target for Messages when setting synchronization barriers. In short, a synchronization barrier is a Message with an empty target.
4.3.3 This difference is finally reflected in Message.next, the last piece of code:
Message next() {
    //...

    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        //...

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {//碰到同步屏障
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                // do while循环遍历消息链表
                // 跳出循环时,msg指向离表头最近的一个异步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    //...
                } else {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        //将msg从消息链表中移除
                        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;
            }

            //...
        }

        //...
    }
}

It can be seen that if a synchronization barrier is set, messages with empty targets (asynchronous messages) will be processed first. This is a priority mechanism for Handler messages.

4.3.4 Setting up synchronization barriers
mHandler.getLooper().getQueue().postSyncBarrier();
4.3.5 Synchronization barrier application
Android应用框架中为了更快的响应UI刷新事件在ViewRootImpl.scheduleTraversals中使用了同步屏障

Reference

  1. In Tencent Classroom, the courses provided by Xiangxue
  2. Synchronization barrier: https://blog.csdn.net/asdgbc/article/details/79148180

Guess you like

Origin blog.csdn.net/HeartCircle/article/details/108746182