Android Handler 消息机制

常用场景:

子线程发送Message

主线程处理Message

子线程发送消息

java 复制代码
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

不管那种方式发送小时最终都走到 sendMessageAtTime

java 复制代码
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);
    }

通过enqueueMessage将消息按照时间先手插入到MessageQueue中

主线程处理消息

应用启动:

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

main方法中关注Looper.prepareMainLooper()和Looper.loop().

Looper.prepareMainLooper()

java 复制代码
 public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {//Looper的唯一性
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

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));
    }

  private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();//绑定当前线程
    }

  MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
  void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
......
}
  1. prepare创建 Looper 并设置给了sThreadLocal 后面loop中要获取

2.prepare创建不可退出的MessageQueue(因为在主线程)

Looper.loop()

java 复制代码
 public static void loop() {
        final Looper me = myLooper();
       ......
        me.mInLoop = true;
        final MessageQueue queue = me.mQueue;
        for (;;) { //死循环
            Message msg = queue.next(); // might block
           ......
            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);
                }
            }
           ......
            msg.recycleUnchecked();
        }
    }

  public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

1.Looper持有了当前线程的MessageQueue

2.通过queue.next() 获取当前时间下一次要执行的Message

3.处理消息 msg.target.dispatchMessage(msg) 分发到内部类中处理(msg.target就是当前消息绑定的Handler)

  1. 消息回收(消息复用) msg.recycleUnchecked()

流程:应用启动-->ActivityThread main 启动 --> 准备Looper --> Looper死循环 一直取队列中的消息 -->处理消息 --> 消息回收处理

PS: 应用异常(Runtime)时压栈最底下ActivityThread.main() 倒数第二行Looper.loop() 这也印证了启动的顺序

Handler创建

常见创建:

java 复制代码
Handler handler=new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};

 public Handler() {
    this(null, false);
 }

public Handler(@Nullable Callback callback, boolean async) {
    
        mLooper = Looper.myLooper();//获取Looper
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;//获取消息队列
        mCallback = callback;//注册回调
        mAsynchronous = async;
    }


 public interface Callback {
        boolean handleMessage(@NonNull Message msg);
    }

消息创建

new Message

java 复制代码
   /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
    */
    public Message() {
    }

Google 没有详细说明new Message 当通过我们实际直接new 就完事了无需多言,不过Google推荐使用Message.obtain() 去创建

java 复制代码
方法一:
Message msg = Message.obtain()
--------------------------------源码-------------------------------
    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();
    }
方法二:
Handler handler = new Handler();
Message msg = handler.obtainMessage();//绑定了handler
--------------------------------源码-------------------------------
public final Message obtainMessage()
    {
        return Message.obtain(this);// this==handler
    }

 public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;

        return m;
    }

不管那种方式创建最终都是走无参的obtain方法

1.sPool等于空时通过new 创建Message

2.sPool 是在Looper dispatch 出去后通过recycleUnchecked清空后到Message

3.sPool最前面的空Messsage返回回去,sPool指针后移队列到下一个保证下一个obtain可以正确获获取到sPool中的空Message

Message和handler的绑定

1.可以在创建Message是绑定,参考Message创建的方法二

2.消息发送是绑定

回到发送Message

java 复制代码
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //绑定动作
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

总结:

handler 机制就是一个传送带
1 ) MessageQueue 就是堆放货物履带。
2 )Activity Thread电机给履带滚动的动力
3)loop中的死循环就像开关,死循环开启履带滚动 轮询 messageQueue按照时间分发出去
4 )Message就是要传输得货物

同步问题

Handler是如何保证自己是线程安全?

从总结图可以看出整个过程只要保证MessageQueue 中msg的入队和出队即可

enqueueMessage方法中 通过synchronized (this) {} 锁住了关键的代码块

1,synchronized 是内置锁 锁的lock 和unlock 是系统(JVM)执行

  1. 锁的是this,就相当于锁的是 MessageQueue 相当于 调用同一个MessageQueue的对象是互斥的

  2. 一个线程持有一个Looper,一个Looper持有一个MessageQueue

所以:主线程只有一个MessageQueue子线程 发送消息的时候,子线程一次只能处理一个消息,其他的消息需要等锁,这样就保证了不管有多少子线程发送消息 主线程的MessageQueue时刻始终只处理一个消息的入队

next() 方法中 同样通过synchronized (this) {} 锁住了按照时间节点返回消息的关键代码

既然这里都是一直要放回当前队列的时间最靠前的msg(头消息),加锁的意义在哪里?

这里就是 锁this的魅力 锁住了MessgaeQueue,锁的范围是所有 this正在访问的代码块都会有保护作用,即代表next方法和enqueueMessage方法能够实现互斥统一时间MessageQueue只能入队或者出队这样就保证了MessageQueue的有序性。

HandlerThread

首先我们看下下面这段代码创建Handler

java 复制代码
Thread thread = new Thread(new Runnable() {
    Looper looper;
@Override
public void run() {
    Looper.prepare();
    looper =Looper.myLooper();
    Looper.loop();
}
public Looper getLooper() {
    return looper;
}
});
    thread.start();
    Handler handler = new Handler(thread.getLooper());

子线程去获取Looper对象然后通过子线程拿到Looper,这段代码看似么有问题其实有雷运行时可能是出现new Hanlder 中参数Looper 为空

1.可能在执行new Handler对象时子线程没有走完导致looper没有赋值完成

解决:thread.start 后延时去new Handler 从而保证looper不为空,但此时线程依旧是不安全的

看看Goolge是如何解决子线程获取Looper 且线程安全?

java 复制代码
public class HandlerThread extends Thread {
    Looper mLooper;
    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

1.HandlerThread 是Thread的子类 new HandlerThread start 执行其run方法

prepare 后通过获取Looper关键代码

synchronized (this) {

mLooper = Looper.myLooper();

notifyAll();

}

getLooper关键代码

synchronized (this) {

while (isAlive() && mLooper == null) {

try {

wait();

} catch (InterruptedException e) {

}

}

}

1.通过synchronized (this) 实现了run和getLooper的互斥

2.两种情况

2.1 :run 先拿到锁

synchronized锁住的代码先执行,完完全全拿到Looper后通过notifyAll()通知所有 HandlerThread 对象 结束等锁,准备拿锁 ,然后释放锁

2.2:getLooper 先拿到锁

此时当线程活着时mLooper肯定为空线程执行wait() 等待且释放锁,getLooper 释放锁的同事run方法就会持有锁,因为HandlerThread 就这两个方法会获取锁。
如果是sleep() 线程会阻塞 不会释放锁。

此类写法就保证了不管什么样的情况下当你通过HandlerThread 去getLooper是一定能获取到线程的唯一Looper.此时线程是安全的。

消息机制之同步屏障

揭秘 Android 消息机制之同步屏障:target==null ?我们知道,Android的消息机制就是Handle - 掘金

相关推荐
黄林晴2 小时前
如何判断手机是否是纯血鸿蒙系统
android
火柴就是我3 小时前
flutter 之真手势冲突处理
android·flutter
法的空间3 小时前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
循环不息优化不止3 小时前
深入解析安卓 Handle 机制
android
恋猫de小郭3 小时前
Android 将强制应用使用主题图标,你怎么看?
android·前端·flutter
jctech3 小时前
这才是2025年的插件化!ComboLite 2.0:为Compose开发者带来极致“爽”感
android·开源
用户2018792831673 小时前
为何Handler的postDelayed不适合精准定时任务?
android
叽哥4 小时前
Kotlin学习第 8 课:Kotlin 进阶特性:简化代码与提升效率
android·java·kotlin
Cui晨4 小时前
Android RecyclerView展示List<View> Adapter的数据源使用View
android
氦客4 小时前
Android Doze低电耗休眠模式 与 WorkManager
android·suspend·休眠模式·workmanager·doze·低功耗模式·state_doze