Handler源码解析及一些重要问题
  JKAkDL5PppSO 2023年11月02日 117 0


资料

Handler源码解析Handler机制及原理探究
Handler机制——同步屏障

享学堂课程

设计思路

设计模式

异步消息和同步消息

消息屏幕

epoll

·

···

Handler源码解析及一些重要问题_接口回调


for(){
 if(msg == null) {
 return ;//什么时候返回空?
 }
 }

应用退出

Handler源码解析及一些重要问题_面试需要_02


Handler源码解析及一些重要问题_接口回调_03

延迟消息最后怎么处理?
插入排序

ThreadLocal

面试题

Handler源码解析及一些重要问题_阻塞队列_04

  1. handler可以创建n个
  2. 一个looper
1. 2.  enqueueMessage{
 msg.target = this; // 持有了handler --》持有了activity —> 导致GC不能回收
 }
  1. 泄露, 持有了外部的this。
  2. Handler源码解析及一些重要问题_接口回调_05

  3. 内部类持有了外部类的对象-----java编程思想

  4. looper.prepare / looper.loop()
  5. quit() 必须quit
    生产者-消费者 模式
  6. Handler源码解析及一些重要问题_ide_06

  7. blockingqueue : 阻塞队列

两个方面阻塞
… nativePollOnce 不到时间,自动唤醒
… messagequeue为空, 无限等待状态

Handler源码解析及一些重要问题_阻塞队列_07

  1. 锁 锁:synchronized:内置锁?
    取得时候,可能会有人插队,所以取也得加锁。


Handler源码解析及一些重要问题_面试需要_08


Handler源码解析及一些重要问题_面试需要_09

一、Message(消息)

得到Message

new Message(){}
static Message obtain()
static Message obtain(Message orig)
static Message obtain(Handler h)
static Message obtain(Handler h, Runnable callback)
static Message obtain(Handler h, int what)
static Message obtain(Handler h, int what, Object obj)
static Message obtain(Handler h, int what, int arg1, int arg2)
static Message obtain(Handler h, int what, int arg1, int arg2, Object obj)
public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;

	// 从全局池返回一个新的消息实例,允许我们在许多情况下避免分配新对象
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
    
public static Message obtain(Handler h, Runnable callback) {
    Message m = obtain();
    m.target = h;
    m.callback = callback;
    return m;
}

二、Handler(处理机)

Handler()
Handler(Callback callback)
Handler(boolean async)
Handler(Callback callback, boolean async)
Handler(Looper looper)
Handler(Looper looper, Callback callback)
Handler(Looper looper, Callback callback, boolean async)
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
public Handler(@Nullable 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(); // 返回与当前线程关联的Looper对象,在后面Looper会讲到
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue; // 返回Looper对象的消息队列,在后面MessageQueue会讲到
    mCallback = callback; // 接口回调
    mAsynchronous = async; // 是否异步
}
public interface Callback {
    boolean handleMessage(@NonNull Message msg);
}

Handler#sendMessage

Handler源码解析及一些重要问题_ide_10

public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

Handler#dispatchMessage()

public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

总结:

  1. 如果msg.callback不为空, 先调用Message中的Callback
  2. 再如果Handler的callback不为空, 就调用callback。handleMessage
  3. 最后才dialing本身的handleMessage (子类必须实现handleMessage)

三、MessageQueue

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }

            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse(); // 标记为使用中
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) { // 当作一个节点
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr); // 唤醒
            }
        }
        return true;
    }

四、Looper

private static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) {
        Message msg = me.mQueue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return false;
        }

        // This must be in a local variable, in case a UI event sets the logger
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " "
                    + msg.callback + ": " + msg.what);
        }
        // Make sure the observer won't change while processing a transaction.
        final Observer observer = sObserver;

        final long traceTag = me.mTraceTag;
        long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
        long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
        if (thresholdOverride > 0) {
            slowDispatchThresholdMs = thresholdOverride;
            slowDeliveryThresholdMs = thresholdOverride;
        }
        final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
        final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

        final boolean needStartTime = logSlowDelivery || logSlowDispatch;
        final boolean needEndTime = logSlowDispatch;

        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }

        final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
        final long dispatchEnd;
        Object token = null;
        if (observer != null) {
            token = observer.messageDispatchStarting();
        }
        long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
        try {
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            if (observer != null) {
                observer.dispatchingThrewException(token, msg, exception);
            }
            throw exception;
        } finally {
            ThreadLocalWorkSource.restore(origWorkSource);
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        if (logSlowDelivery) {
            if (me.mSlowDeliveryDetected) {
                if ((dispatchStart - msg.when) <= 10) {
                    Slog.w(TAG, "Drained");
                    me.mSlowDeliveryDetected = false;
                }
            } else {
                if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                        msg)) {
                    // Once we write a slow delivery log, suppress until the queue drains.
                    me.mSlowDeliveryDetected = true;
                }
            }
        }
        if (logSlowDispatch) {
            showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", 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.recycleUnchecked();

        return true;
    }

笔记

  1. enqueuMessage时候,就进行优先级排序, 每个Message都有时间(或自动加上)
  2. 初始化Looper的时候,使用ThreadLocal保存
  3. MessageQueue无限加入(因为系统也要用)
  4. nativeWaitPollOnce, 有限等待,无限等待(入队唤醒)
  5. MessageQueue#quit, 唤醒线程、队列=null
  6. 入队的时候加锁, 出队的时候加锁(避免此时入队)
  7. 放入内存中(Queue), 然后在主线程中的Loop取出来。
  8. 同步屏障就是把队列的头节点的target设置为null, 然后只有同步的才执行

常见面试题

Handler的实现原理

从四个方面看Handler、Message、MessageQueue 和 Looper

  • Handler:负责消息的发送和处理 Message:消息对象,类似于链表的一个结点;
  • MessageQueue:消息队列,用于存放消息对象的数据结构;
  • Looper:消息队列的处理者(用于轮询消息队列的消息对象)

Handler发送消息时调用MessageQueue的enqueueMessage插入一条信息到
MessageQueue,Looper不断轮询调用MeaasgaQueue的next方法 如果发现message就调用handler的
dispatchMessage,dispatchMessage被成功调用,接着调用handlerMessage()。

子线程中能不能直接new一个Handler,为什么主线程可以

主线程的Looper第一次调用loop方法,什么时候,哪个类
不能,因为Handler 的构造方法中,会通过
Looper.myLooper()获取looper对象,如果为空,则抛出异常, 主线程则因为已在入口处ActivityThread的main方
法中通过 Looper.prepareMainLooper()获取到这个对象, 并通过 Looper.loop()开启循环,在子线程中若要使用
handler,可先通过Loop.prepare获取到looper对象,并使用Looper.loop()开启循环

Handler导致的内存泄露原因及其解决方案

原因:
1.Java中非静态内部类和匿名内部类都会隐式持有当前类的外部引用
2.我们在Activity中使用非静态内部类初始化了一个Handler,此Handler就会持有当前Activity的引用。
3.我们想要一个对象被回收,那么前提它不被任何其它对象持有引用,所以当我们Activity页面关闭之后,存在引用关系:“未被处理 / 正处理的消息 -> Handler实例 -> 外部类”,如果在Handler消息队列 还有未处理的消息 / 正在处理消息时 导致Activity不会被回收,从而造成内存泄漏 解
决方案: 1.将Handler的子类设置成 静态内部类,使用WeakReference弱引用持有Activity实例 2.当外部类结束生命周
期时,清空Handler内消息队列

一个线程可以有几个Handler,几个Looper,几个MessageQueue对象

一个线程可以有多个Handler,只有一个Looper对象,只有一个MessageQueue对象。Looper.prepare()函数中知
道,。在Looper的prepare方法中创建了Looper对象,并放入到ThreadLocal中,并通过ThreadLocal来获取looper
的对象, ThreadLocal的内部维护了一个ThreadLocalMap类,ThreadLocalMap是以当前thread做为key的,因此可以得
知,一个线程最多只能有一个Looper对象, 在Looper的构造方法中创建了MessageQueue对象,并赋值给mQueue
字段。因为Looper对象只有一个,那么Messagequeue对象肯定只有一个。

Message对象创建的方式有哪些 & 区别

Message.obtain()怎么维护消息池的
1.Message msg = new Message(); 每次需要Message对象的时候都创建一个新的对象,每次都要去堆内存开辟对象存储空间
2.Message msg = Message.obtain(); obtainMessage能避免重复Message创建对象。它先判断消息池是不是为空,如果非空的话就从消息池表头的Message取走,再把表头指向
next。 如果消息池为空的话说明还没有Message被放进去,那么就new出来一个Message对象。消息池使用
Message 链表结构实现,消息池默认最大值 50。消息在loop中被handler分发消费之后会执行回收的操作,将该消
息内部数据清空并添加到消息链表的表头。
3.Message msg = handler.obtainMessage(); 其内部也是调用的obtain()方法

Handler 有哪些发送消息的方法

sendMessage(Message msg)
sendMessageDelayed(Message msg, long uptimeMillis)
post(Runnable r)
postDelayed(Runnable r, long uptimeMillis)
sendMessageAtTime(Message msg,long when)

Handler的post与sendMessage的区别和应用场景

1.源码
sendMessage sendMessage-sendMessageAtTime-enqueueMessage。
post
sendMessage-getPostMessage-sendMessageAtTime-enqueueMessage getPostMessage会先生成一个
Messgae,并且把runnable赋值给message的callback
2.Looper->dispatchMessage处理时
public void dispatchMessage(@NonNull Message msg) { if (msg.callback != null) { handleCallback(msg); }
else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
dispatchMessage方法中直接执行post中的runnable方法。
而sendMessage中如果mCallback不为null就会调用mCallback.handleMessage(msg)方法,如果handler内的
callback不为空,执行mCallback.handleMessage(msg)这个处理消息并判断返回是否为true,如果返回true,消息
处理结束,如果返回false,handleMessage(msg)处理。否则会直接调用handleMessage方法。
post方法和handleMessage方法的不同在于,区别就是调用post方法的消息是在post传递的Runnable对象的
run方法中处理,而调用sendMessage方法需要重写handleMessage方法或者给handler设置callback,在callback
的handleMessage中处理并返回true
应用场景
post一般用于单个场景 比如单一的倒计时弹框功能 sendMessage的回调需要去实现handleMessage Message
则做为参数 用于多判断条件的场景。

handler postDealy后消息队列有什么变化,假设先 postDelay 10s, 再postDelay 1s, 怎么处理这2条消息sendMessageDelayedsendMessageAtTime-sendMessage

postDelayed传入的时间,会和当前的时间SystemClock.uptimeMillis()做加和,而不是单纯的只是用延时时间。
延时消息会和当前消息队列里的消息头的执行时间做对比,如果比头的时间靠前,则会做为新的消息头,不然则会从
消息头开始向后遍历,找到合适的位置插入延时消息。 postDelay()一个10秒钟的Runnable A、消息进队,
MessageQueue调用nativePollOnce()阻塞,Looper阻塞; 紧接着post()一个Runnable B、消息进队,判断现在A时
间还没到、正在阻塞,把B插入消息队列的头部(A的前面),然后调用nativeWake()方法唤醒线程;
MessageQueue.next()方法被唤醒后,重新开始读取消息链表,第一个消息B无延时,直接返回给Looper; Looper
处理完这个消息再次调用next()方法,MessageQueue继续读取消息链表,第二个消息A还没到时间,计算一下剩余
时间(假如还剩9秒)继续调用nativePollOnce()阻塞; 直到阻塞时间到或者下一次有Message进队;

MessageQueue是什么数据结构

内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表 这点和传统的队列有点不一样,
主要区别在于Android的这个队列中的消息是按照时间先后顺序来存储的,时间较早的消息,越靠近队头。 当然,我
们也可以理解成,它是先进先出的,只是这里的先依据的不是谁先入队,而是消息待发送的时间

Handler怎么做到的一个线程对应一个Looper,如何保证只有一个MessageQueue ThreadLocal在Handler机制中的作用

设计的初衷是为了解决多线程编程中的资源共享问题, synchronized采取的是“以时间换空间”的策略,本质上是
对关键资源上锁,让大家排队操作。 而ThreadLocal采取的是“以空间换时间”的思路, 它一个线程内部的数据存储
类,通过它可以在制定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据, 对于其他线
程就获取不到数据,可以保证本线程任何时间操纵的都是同一个对象。比如对于Handler,它要获取当前线程的
Looper,很显然Looper的作用域就是线程,并且不同线程具有不同的Looper。 ThreadLocal本质是操作线程中
ThreadLocalMap来实现本地线程变量的存储的 ThreadLocalMap是采用数组的方式来存储数据,其中key(弱引用)指
向当前ThreadLocal对象,value为设的值 通过ThreadLocal计算出Hash key,通过这个哈 ThreadLocal对象,value
为设的值

HandlerThread是什么 & 好处 &原理 & 使用场景

HHandlerThread本质上是一个线程类,它继承了Thread; HandlerThread有自己的内部Looper对象,通过
Looper.loop()进行looper循环; 通过获取HandlerThread的looper对象传递给Handler对象,然后在
handleMessage()方法中执行异步任务;
优势: 1.将loop运行在子线程中处理,减轻了主线程的压力,使主线程更流畅,有自己的消息队列,不会干扰UI线程 2.
串行执行,开启一个线程起到多个线程的作用
劣势: 1.由于每一个任务队列逐步执行,一旦队列耗时过长,消息延时 2.对于IO等操作,线程等待,不能并发
我们可以使用HandlerThread处理本地IO读写操作(数据库,文件),因为本地IO操作大多数的耗时属于毫秒级
别,对于单线程 + 异步队列的形式 不会产生较大的阻塞

IdleHandler及其使用场景

Handler 机制提供的一种,可以在 Looper 事件循环的过程中,当出现空闲的时候,允许我们执行任务的一种机
制. IdleHandler在looper里面的message处理完了的时候去调用 怎么使用 IdleHandler 被定义在 MessageQueue
中,它是一个接口. 定义时需要实现其 queueIdle() 方法。返回值为 true 表示是一个持久的 IdleHandler 会重复使
用,返回 false 表示是一个一次性的 IdleHandler。 IdleHandler 被 MessageQueue 管理,对应的提供了
addIdleHandler() 和 removeIdleHandler() 方法。将其存入 mIdleHandle addIdleHandler() 和 removeIdleHandler() 方法。将其存入 mIdleHandlers 这个 ArrayList 中。 什么时候掉用 就在MessageQueue的
next方法里面。 MessageQueue 为空,没有 Message; MessageQueue 中最近待处理的 Message,是一个延迟
消息(when>currentTime),需要滞后执行; 使用场景 1.Activity启动优化:onCreate,onStart,onResume中
耗时较短但非必要的代码可以放到IdleHandler中执行,减少启动时间
2.想要在一个View绘制完成之后添加其他依赖于这个View的View,当然这个用View#post()也能实现,区别就是
前者会在消息队列空闲时执行 优化页面的启动,较复杂的view填充 填充里面的数据界面view绘制之前的话,就会出现
以上的效果了,view先是白的,再出现. app的进程其实是ActivityThread,performResumeActivity先回调
onResume , 之后 执行view绘制的measure, layout, draw,也就是说onResume的方法是在绘制之前,在
onResume中做一些耗时操作都会影响启动时间 把在onResume以及其之前的调用的但非必须的事件(如某些界面
View的绘制)挪出来找一个时机(即绘制完成以后)去调用即可。

消息屏障,同步屏障机制what

同步屏障只在Looper死循环获取待处理消息时才会起作用,也就是说同步屏障在MessageQueue.next函数中发
挥着作用。
在next()方法中,有一个屏障的概念(message.target ==null为屏障消息), 遇到target为null的Message,说明是
同步屏障,循环遍历找出一条异步消息,然后处理。 在同步屏障没移除前,只会处理异步消息,处理完所有的异步
消息后,就会处于堵塞 当出现屏障的时候,会滤过同步消息,而是直接获取其中的异步消息并返回, 就是这样来实现
「异步消息优先执行」的功能
how
1、Handler构造方法中传入async参数,设置为true,使用此Handler添加的Message都是异步的; 2、创建
Message对象时,直接调用setAsynchronous(true) 3.removeSyncBarrier() 移除同步屏障:
应用
在 View 更新时,draw、requestLayout、invalidate 等很多地方都调用了ViewRootImpl#scheduleTraversals(
Android应用框架中为了更快的响应UI刷新事件在ViewRootImpl.scheduleTraversals中使用了同步屏障

子线程能不能更新UI

刷新UI, 都会调用到ViewRootImpl.Android每次刷新UI的时候,最终根布局ViewRootImpl.checkThread()来检
验线程是否是View的创建线程。 ViewRootImpl创建的第一个地方,从Acitivity声明周期handleResumeActivity会被
优先调用到,也就是说在OnResume后ViewRootImpl就被创建,这个时候无法在在子线程中访问UI了,上面子线程
延迟了一会,handleResumeActivity已经被调用了,所以发生了崩溃 不延迟在creae里直接设置不会崩溃 线程更新
UI也行,但是只能更新自己创建的View

为什么Android系统不建议子线程访问UI

在android中子线程可以有好多个,但是如果每个线程都可以对ui进行访问,我们的界面可能就会变得混乱不
堪,这样多个线程操作同一资源就会造成线程安全问题,当然,需要解决线程安全问题的时候,我们第一想到的可能
就是加锁,但是加锁会降低运行效率,所以android出于性能的考虑,并没有使用加锁来进行ui操作的控制。

Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

MessageQueue#next 在没有消息的时候会阻塞,如何恢复?
他不阻塞的原因是epoll机制,他是linux里面的,在native层会有一个读取端和一个写入端,当有消息发送过来
的时候会去唤醒读取端,然后进行消息发送与处理,没消息的时候是处于休眠状态,所以他不会阻塞他。

Handler消息机制中,一个looper是如何区分多个Handler的当Activity有多个Handler的时候,怎么样区分当前消息由哪个Handler处理处理message的时候怎么知道是去哪个callback处理的

每个Handler会被添加到 Message 的target字段上面,Looper 通过调用 Message.target.handleMessage() 来 让 Handler 处理消息。

Looper.quit/quitSafely的区别

当我们调用Looper的quit方法时,实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法
的作用是把MessageQueue消息池中所有的消息全部清空, 无论是延迟消息(延迟消息是指通过
sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。
当我们调用Looper的quitSafely方法时,实际上执行了MessageQueue中的removeAllFutureMessagesLocked
方法,通过名字就可以看出,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延
迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消
息。

通过Handler如何实现线程的切换

当在A线程中创建handler的时候,同时创建了MessageQueue与Looper,Looper在A线程中调用loop进入一个
无限的for循环从MessageQueue中取消息,当B线程调用handler发送一个message的时候,会通过
msg.target.dispatchMessage(msg);将message插入到handler对应的MessageQueue中,Looper发现有message
插入到MessageQueue中,便取出message执行相应的逻辑,因为Looper.loop()是在A线程中启动的,所以则回到
了A线程,达到了从B线程切换到A线程的目的。

Handler 如何与 Looper 关联的

通过构造方法 mLooper = Looper.myLooper()->sThreadLocal.get()( sThreadLocal.set)

Looper 如何与 Thread 关联的

Looper 与 Thread 之间是通过 ThreadLocal 关联的,这个可以看 Looper#prepare() 方法 Looper 中有一个
ThreadLocal 类型的 sThreadLocal静态字段,Looper通过它的 get 和 set 方法来赋值和取值。 由于 ThreadLocal是
与线程绑定的,所以我们只要把 Looper 与 ThreadLocal 绑定了,那 Looper 和 Thread 也就关联上了

Looper.loop()源码

for无限循环,阻塞于消息队列的next方法 取出消息后调用msg.target.dispatchMessage(msg)进行消息分发

MessageQueue的enqueueMessage()方法如何进行线程同步的

就是单链表的插入操作 如果消息队列被阻塞回调用nativeWake去唤醒。 用synchronized代码块去进行同步。

MessageQueue的next()方法内部原理

next() 是如何处理一般消息的?
next() 是如何处理同步屏障的?
next() 是如何处理延迟消息的?
调用 MessageQueue.next() 方法的时候会调用 Native 层的 nativePollOnce() 方法进行精准时间的阻塞。在
Native 层,将进入 pullInner() 方法,使用 epoll_wait 阻塞等待以读取管道的通知。如果没有从 Native 层得到消
息,那么这个方法就不会返回。此时主线程会释放 CPU 资源进入休眠状态。

子线程中是否可以用MainLooper去创建Handler,Looper和Handler是否一定处于一个线程

可以的。 子线程中Handler handler = new Handler(Looper.getMainLooper());,此时两者就不在一个线程中

ANR和Handler的联系

Handler是线程间通讯的机制,Android中,网络访问、文件处理等耗时操作必须放到子线程中去执行,否则将
会造成ANR异常。 ANR异常:Application Not Response 应用程序无响应 产生ANR异常的原因:在主线程执行了耗
时操作,对Activity来说,主线程阻塞5秒将造成ANR异常,对BroadcastReceiver来说,主线程阻塞10秒将会造成
ANR异常。 解决ANR异常的方法:耗时操作都在子线程中去执行 但是,Android不允许在子线程去修改UI,可我们
又有在子线程去修改UI的需求,因此需要借助Handler。


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  qSqNY1UH2lvR   2023年11月02日   57   0   0 链路权重ide
  mrMLeSzj1mp0   2023年11月02日   31   0   0 文件名上传ide
  01BFOGI7NzGp   2023年11月02日   85   0   0 nginxluanginx location ifide
  Fv5flEkOgYS5   2023年11月02日   53   0   0 i++javaide
JKAkDL5PppSO