Android Handler

一、Android Handler介绍

Android Handler是一种事件驱动模型,用于实现线程切换或执行延时任务。它在整个Android开发体系中占据着很重要的地位,可以用于保证多个任务在执行时的有序性。在Android系统中,主线程有特殊地位,因此像EventBus和Retrofit这类并非Android独有的第三方库,都是通过Handler来实现对Android系统的特殊平台支持。

Android Handler的主要作用是将消息(Message)或Runnable对象发送到主线程的消息队列中,以便在主线程中执行。它可以通过post()方法将消息或Runnable对象发送到消息队列中,也可以通过sendMessage()方法将消息发送到消息队列中。当消息或Runnable对象被发送到消息队列中时,Handler会自动将它们封装成Message对象,并将它们添加到消息队列中。当主线程空闲时,Handler会从消息队列中取出消息或Runnable对象,并在主线程中执行它们。

以Handler的sendMessage方法为例,当发送一个消息后,会将此消息加入消息队列MessageQueue中。Looper负责去遍历消息队列并且将队列中的消息分发给对应的Handler进行处理。在Handler的handleMessage方法中处理该消息,这就完成了一个消息的发送和处理过程。

Handler机制的机制原理

Android中采用的是Linux中的管道通信,关于管道,简单来说,管道就是一个文件,在管道的两端,分别是两个打开文件文件描述符,这两个打开文件描述符都是对应同一个文件,其中一个是用来读的,别一个是用来写的。消息队列创建时,调用JNI函数,初始化NativeMessageQueue对象。NativeMessageQueue则会初始化Looper对象。Looper的作用就是,当Java层的消息队列中没有消息时,就使Android应用程序主线程进入等待状态,而当Java层的消息队列中来了新的消息后,就唤醒Android应用程序的主线程来处理这个消息。



Handler通过sendMessage()发送Message到MessageQueue队列。Looper通过loop(),不断提取出达到触发条件的Message,并将Message交给target来处理。经过dispatchMessage()后,交回给Handler的handleMessage()来进行相应地处理。将Message加入MessageQueue时,处往管道写入字符,可以会唤醒loop线程;如果MessageQueue中- 没有Message,并处于Idle状态,则会执行IdelHandler接口中的方法,往往用于做一些清理性地工作。

管道,其本质是也是文件,但又和普通的文件会有所不同:管道缓冲区大小一般为1页,即4K字节。管道分为读端和写端,读端负责从管道拿数据,当数据为空时则阻塞;写端向管道写数据,当管道缓存区满时则阻塞。

在Handler机制中,Looper.loop方法会不断循环处理Message,其中消息的获取是通过 Message msg = queue.next(); 方法获取下一条消息。该方法中会调用nativePollOnce()方法,这便是一个native方法,再通过JNI调用进入Native层,在Native层的代码中便采用了管道机制。

我们知道线程之间内存共享,通过Handler通信,消息池的内容并不需要从一个线程拷贝到另一个线程,因为两线程可使用的内存时同一个区域,都有权直接访问,当然也存在线程私有区域ThreadLocal(这里不涉及)。即然不需要拷贝内存,那管道是何作用呢?

Handler机制中管道作用就是当一个线程A准备好Message,并放入消息池,这时需要通知另一个线程B去处理这个消息。线程A向管道的写端写入数据1(对于老的Android版本是写入字符W),管道有数据便会唤醒线程B去处理消息。管道主要工作是用于通知另一个线程的,这便是最核心的作用。

为什么采用管道而非Binder?

从内存角度:通信过程中Binder还涉及一次内存拷贝,handler机制中的Message根本不需要拷贝,本身就是在同一个内存。Handler需要的仅仅是告诉另一个线程数据有了。

从CPU角度,为了Binder通信底层驱动还需要为何一个binder线程池,每次通信涉及binder线程的创建和内存分配等比较浪费CPU资源。

Handler与MessageQueue、Message、Looper直之间的关系

Handler是用于线程间的通信,它可以将消息(Message)发生到MessageQueue中,然后在指定的线程中接收并处理这些消息。

MessageQueue是用于储存消息的队列,每个线程都有一个MessageQueue,用于存储等待处理的消息。

Message用于在线程之间传递消息,它包含了一些需要传递的信息以及Handler对象。

Looper是用于循环读取MessageQueue中的消息并将它们交给Handler来处理的对象,它只能存在一个线程中,通常是主线程。

Message是承载任务的载体,在Handler机制中贯穿始终,Handler则是对外公开的API,负责发送Message和处理任务的回调,是Message的生产者,Message负责管理待处理Message的入队和出队,是Messae的容器,Looper负责轮循MessageQueue,保持线程持续运行任务,是Message的消费者。

因此,当我们在一个线程中使用了Handler,他会向该线程的MessageQueue中发生消息,然后由Looper在该线程中循环读取该MessageQueue中的消息,并将消息交给Handler来处理。这些机制使得我们可以很方便地实现线程间得通信和操作UI等操

作。 Handler 的背后有着 Looper 以及 MessageQueue 的协助,三者通力合作,分工明确。

Looper :负责关联线程以及消息的分发在该线程下从 MessageQueue 获取 Message,分发给Handler ;

MessageQueue :是个队列,负责消息的存储与管理,负责管理由 Handler 发送过来的Message;

Handler : 负责发送并处理消息,面向开发者,提供 API,并隐藏背后实现的细节。

Handler 发送的消息由 MessageQueue 存储管理,并由 Loopler 负责回调消息到 handleMessage()。

线程的转换由 Looper 完成,handleMessage() 所在线程由 Looper.loop() 调用者所在线程决

二、Android Handler相关类

JAVA类

Handler

Handler类是Android中的一个类,它主要用于在不同的线程之间传递和处理消息。它可以将消息和Runnable对象安排到MessageQueue中,以便在将来的某个时间点处理它们。Handler类通常用于更新UI,因为它可以将消息发送到UI线程以更新UI元素。

Handler代码位于:

freameworks/base/core/java/android/os/Handler.java

Handler的定义:

public class Handler {
    public interface Callback {
    public void handleMessage(@NonNull Message msg) {}
    }
    private final class MessengerImpl extends IMessenger.Stub {}
    private static final class BlockingRunnable implements Runnable {}
}

Handler 方法:

public Handler() :构造方法
public Handler(boolean async):构造方法
public Handler(@NonNull Looper looper) :构造方法
public Handler(@Nullable Callback callback):构造方法
public Handler(@Nullable Callback callback, boolean async):构造方法
public Handler(@NonNull Looper looper, @Nullable Callback callback):构造方法
public void handleMessage(@NonNull Message msg):消息接收方法,子类必须实现
public void dispatchMessage(@NonNull Message msg):用于处理消息队列中的消息。当消息队列中有消息时,Handler会调用dispatchMessage()方法来处理消息。
public static Handler createAsync(@NonNull Looper looper):创建一个新的处理程序,其发布的消息和可运行对象不受同步屏障(如显示垂直同步)的约束。
public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback):创建一个新的处理程序,其发布的消息和可运行对象不受同步屏障(如显示垂直同步)的约束。
public String getMessageName(@NonNull Message message):返回一个字符串,该字符串表示指定消息的名称。
public final Message obtainMessage():从全局消息池中返回新的Message。
public final Message obtainMessage(int what):从全局消息池中返回新的Message。
public final Message obtainMessage(int what, @Nullable Object obj):从全局消息池中返回新的Message。
public final Message obtainMessage(int what, int arg1, int arg2):从全局消息池中返回新的Message。
public final Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj):从全局消息池中返回新的Message。
public final boolean post(@NonNull Runnable r):使 Runnable r 添加到消息队列中。
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis):使 Runnable r 添加到消息队列中,在指定的特定时间运行。
public final boolean postAtTime(@NonNull Runnable r, @Nullable Object token, long uptimeMillis):使 Runnable r 添加到消息队列中,在指定的特定时间运行。
public final boolean postDelayed(@NonNull Runnable r, long delayMillis):使 Runnable r 添加到消息队列中,在经过指定的时间后运行。
public final boolean postDelayed(@NonNull Runnable r, @Nullable Object token, long delayMillis):使 Runnable r 添加到消息队列中,在经过指定的时间后运行。
public final boolean postAtFrontOfQueue(@NonNull Runnable r) :将消息发布到实现 Runnable 的对象。
public final boolean runWithScissors(@NonNull Runnable r, long timeout):同步运行指定的任务。
public final void removeCallbacks(@NonNull Runnable r):删除消息队列中任何待处理的 Runnable r post。
public final void removeCallbacks(@NonNull Runnable r, @Nullable Object token):删除消息队列中任何待处理的 Runnable r post。
public final boolean sendMessage(@NonNull Message msg) :发送消息
public final boolean sendEmptyMessage(int what):发送仅包含 what 值的消息。
public final boolean sendEmptyMessageDelayed(int what, long delayMillis):发送仅包含 what 值的消息,该值将在指定的时间量过后传递。
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis):发送仅包含要在特定时间传递的 what 值的消息。
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis):将消息排入消息队列,在之前的所有待处理消息之后(当前时间 + 延迟Millis)。
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis):在绝对时间(以毫秒为单位)uptimeMillis之前,将消息排入消息队列。
public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg):将消息排在消息队列的前面,以便在消息循环的下一次迭代中进行处理。
public final boolean executeOrSendMessage(@NonNull Message msg):如果在此处理程序对应的同一线程上调用消息,则同步执行消息,否则 将其推送到队列
public final void removeMessages(int what):删除消息队列中任何带有代码“what”的待处理消息。
public final void removeMessages(int what, @Nullable Object object):删除消息队列中任何代码为“what”且其对象为“object”的待处理消息。
public final void removeEqualMessages(int what, @Nullable Object object):删除消息队列中任何代码为“what”且其对象为“object”的待处理消息。
public final void removeCallbacksAndMessages(@Nullable Object token):删除任何待处理的回调和已发送的obj 为令牌的消息。
public final void removeCallbacksAndEqualMessages(@Nullable Object token):删除任何待处理的回调和已发送的obj为令牌的消息。
public final boolean hasMessages(int what):检查消息队列中是否有任何代码为“what”的待处理消息发布。
public final boolean hasMessagesOrCallbacks():返回此处理程序上当前是否计划了任何消息或回调。
public final boolean hasMessages(int what, @Nullable Object object):检查消息队列中是否有任何代码为“what”且其 obj 为“object”的待处理消息发布。
public final boolean hasEqualMessages(int what, @Nullable Object object):检查消息队列中是否有任何代码为“what”且其 obj 为“object”的待处理消息发布。
public final boolean hasCallbacks(@NonNull Runnable r):检查消息队列中是否有任何带有回调 r 的待处理消息发布。
public final Looper getLooper():如果我们能摆脱这个方法,处理程序不需要记住它的循环,我们可以导出一个 getMessageQueue() 方法......

MessageQueue

Android中的MessageQueue类是一个消息队列,用于存储和处理Message对象。它是Looper类的一部分,用于在Android应用程序中实现线程间通信。

MessageQueue代码位于:

freameworks/base/core/java/android/os/MessageQueue.java

MessageQueue的定义:

public final class MessageQueue {
    private static final class FileDescriptorRecord {}
}

方法:

public boolean isIdle():如果循环器没有待处理的挂起消息,则返回 true。
public void addIdleHandler(@NonNull IdleHandler handler):将新的IdleHandler添加到此消息队列。
public void removeIdleHandler(@NonNull IdleHandler handler):从之前使用添加的队列中删除IdleHandler。
public boolean isPolling() :返回此循环程序的线程当前是否正在轮询更多要执行的工作。
public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd, @OnFileDescriptorEventListener.Events int events, @NonNull OnFileDescriptorEventListener listener):添加文件描述符侦听器,以便在发生文件描述符相关事件时接收通知。
public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd):删除文件描述符侦听器。
boolean enqueueMessage(Message msg, long when) :将消息加入消息队列中等待处理

Message

Android的Message是一个用于在线程之间传递信息的类,它包含了对消息的描述和任意的数据对象。

Message代码位于:

freameworks/base/core/java/android/os/Message.java

Message的定义:

public final class Message implements Parcelable {
    public int what; //消息代码
    public int arg1; //消息参数
    public int arg2; //消息参数
    public Object obj; //可以存放任意对象
    public Messenger replyTo; //可选的 Messenger,可以在其中发送对此消息的回复。
    public int sendingUid = UID_NONE;
    public int workSourceUid = UID_NONE;
}

Message 方法:

public static Message obtain() :从全局池中返回一个新的 Message 实例。
public static Message obtain(Message orig) :从全局池中返回一个新的 Message 实例。
public static Message obtain(Handler h) :从全局池中返回一个新的 Message 实例。
public static Message obtain(Handler h, Runnable callback):从全局池中返回一个新的 Message 实例。
public static Message obtain(Handler h, int what):从全局池中返回一个新的 Message 实例。
public static Message obtain(Handler h, int what, Object obj):从全局池中返回一个新的 Message 实例。
public static Message obtain(Handler h, int what, int arg1, int arg2):从全局池中返回一个新的 Message 实例。
public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj):从全局池中返回一个新的 Message 实例。
public void recycle():将 Message 实例返回到全局池。
public void copyFrom(Message o):使此消息类似于 o。 执行数据字段的浅拷贝。
public long getWhen():返回此邮件的目标传递时间(以毫秒为单位)。
public Handler getTarget() :检索将接收此消息的Handler实现。
public Runnable getCallback():检索在处理此消息时将执行的回调对象。
public Message setCallback(Runnable r):检索在处理此消息时将执行的回调对象。
public Bundle getData():获取与此事件关联的任意数据的 Bundle,并在必要时延迟创建它。
public Bundle peekData():像 getData() 一样,但不会懒惰地创建 Bundle。
public void setData(Bundle data):设置任意数据值的 Bundle。如果可以,请使用 arg1 和 arg2 成员作为发送一些简单整数值的低成本方式。
public void sendToTarget():将此消息发送到 {@link #getTarget} 指定的处理程序。
public boolean isAsynchronous():如果消息是异步的,则返回 true,这意味着它不受 {@link Looper} 同步屏障的约束。
public void setAsynchronous(boolean async) :设置消息是否为异步消息,这意味着它不受 {@link Looper} 同步屏障的约束。

Looper

Android的Looper是Android系统中的一个类,它是一个线程的消息循环器,用于处理消息队列中的消息。

Looper代码位于:

freameworks/base/core/java/android/os/Looper.java

Looper的定义:

public final class Looper {}

Looper 方法:

public static void prepare():将当前线程初始化为循环器。
public static void prepareMainLooper():将当前线程初始化为循环器,将其标记为应用程序的主循环器。
public static Looper getMainLooper():返回应用程序的主循环器,该循环器位于应用程序的主线程中。
public static void setObserver(@Nullable Observer observer):在此过程中为所有 Looper 设置事务观察器。
public static void loop():在此线程中运行消息队列。请务必调用 quit 以结束循环。
public static @Nullable Looper myLooper():返回与当前线程关联的 Looper 对象。 如果调用线程未与 Looper 关联,则返回 null。
public static @NonNull MessageQueue myQueue():返回与当前线程关联的MessageQueue对象。
public boolean isCurrentThread():如果当前线程是此循环程序的线程,则返回 true。
public void setMessageLogging(@Nullable Printer printer):控制此 Looper 处理消息时的日志记录。
public void setSlowLogThresholdMs(long slowDispatchThresholdMs, long slowDeliveryThresholdMs):设置慢速派送/投递日志的阈值。
public void quit():退出Loop。
public void quitSafely() :安全退出Loop。
public @NonNull Thread getThread():获取与此 Looper 关联的线程。
public @NonNull MessageQueue getQueue() :获取此循环程序的消息队列。

C++类

Looper

Android libutils库中的Looper是一个用于实现消息循环的类。它是Android系统中的一个重要组件,用于处理线程间的消息传递和事件分发。

Looper代码位于:

system/core/libutils/Looper.c

system/core/libutils/include/utils/Looper.h

Looper的定义:

struct Message {}
class MessageHandler : public virtual RefBase {
class WeakMessageHandler : public MessageHandler {}
class LooperCallback : public virtual RefBase {}
class SimpleLooperCallback : public LooperCallback {}
class Looper : public RefBase {}

Looper方法:

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) :用于等待并处理事件。
void Looper::wake() :用于唤醒一个处于阻塞状态的Looper线程,使其从阻塞状态中退出,继续执行消息循环。

三、Handler的使用方法

Handler的使用方法请参考:

Android 多线程——Handler的基本使用-CSDN博客

这里再介绍Handler在使用上的注意事项:

(1)一个线程有几个Handler?

多个,通常我们开发过程中就会new出不止一个Handler。

(2)一个线程有几个Looper?如何保证?

仅有1个Looper

Looper的构造是私有的,只有通过其prepare()方法构建出来,当调用了Looper的prepare()方法后,会调用ThreadLocal中的get()方法检查ThreadLocalMap中是否已经set过Looper?

如果有,则会抛出异常,提示每个线程只能有一个Looper,如果没有,则会往ThreadLocalMap中set一个new出来的Looper对象。

这样可以保证ThreadLocalMap和Looper一一对应,即一个ThreadLocalMap只会对应一个Looper。而这里的ThreadLocalMap是在Thread中的一个全局变量,也只会有一个,所以就可以保证一个Thread中只有一个Looper。

(3)Handler内存泄漏的原因?

内部类持有外部的引用。

Handler原理:由于Handler可以发送延迟消息,所以为了保证消息执行完毕后,由同一个Handler接收到,所以发送出去的Message中会持有Handler的引用,这个引用存在Message的target字段中,是Handler所有的sendMessage()方法最后都会调用enqueueMessage(),而在enqueueMessage()中会给Message的target字段赋值this。

因此Message持有Handler的引用,Handler又持有Activity的引用,所以在Message处理完之前,如果Activity被销毁了,就会造成内存泄漏。

怎么解决?可以使用static修饰Handler对象。

(4)为何主线程可以new Handler?如果想要在子线程中new Handler要做些什么准备?

因为在ActivityThread中的main()已经对Looper进行了prepar()操作,所以可以直接在主线程new Handler。

android-28的SystemServer类中:

main方法是整个android应用的入口,在子线程中调用Looper.prepare()是为了创建一个Looper对象,并将该对象存储在当前线程的ThreadLocal中,每个线程都会有一个ThreadLocal,它为每个线程提供了一个本地的副本变量机制,实现了和其它线程隔离,并且这种变量只在本线程的生命周期内起作用,可以减少同一个线程内多个方法之间的公共变量传递的复杂度。Looper.loop()方法是为了取出消息队列中的消息并将消息发送给指定的handler,通过msg.target.dispatchMassage()方法

    /**
     * The main entry point from zygote.
     */
    public static void main(String[] args) {
        new SystemServer().run();
    }




    private void run() {
        ......
        Looper.prepareMainLooper();
        ......
    }

如果想在子线程中new Handler,则需要先手动调用Looper的prepare()方法初始化Looper,再调用Looper的loop()方法使Looper运转。

      new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();			//初始化Looper
                new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                    }
                };
                Looper.loop();
            }
        })

(5)子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?

如果不处理的话,会阻塞线程,处理方案是调用Looper的quitSafely();

quitSafely()会调用MessageQueue的quit()方法,清空所有的Message,并调用nativeWake()方法唤醒之前被阻塞的nativePollOnce(),使得方法next()方法中的for循环继续执行,接下来发现Message为null后就会结束循环,Looper结束。如此便可以释放内存和线程。

(6)内部是如何确保线程安全的?

可以存在多个Handler往MessageQueue中添加数据(发消息时各个Handler可能处于不同线程)

添加消息的方法enqueueMessage()中有synchronize修饰,取消息的方法next()中也有synchronize修饰。

Handler的delay消息(延迟消息)时间准确吗?

由于上述的加锁操作,所以时间不能保证完全准确。

(7)使用Message时应该如何创建它?

使用Message的obtain()方法创建,直接new出来容易造成内存抖动。

内存抖动是由于频繁new对象,gc频繁回收导致,而且由于可能被别的地方持有导致无法及时回收所以会导致内存占用越来越高。

使用obtain()对内存复用,可以避免内存抖动的发生。其内部维护了一个Message池,其是一个链表结构,当调用obtain()的时候会复用表头的Message,然后会指向下一个。如果表头没有可复用的message则会创建一个新的对象,这个对象池的最大长度是50。

(8)使用Handler的postDelay后消息队列会有什么变化?

如果此时消息队列为空,不会执行,会计算消息需要等待的时间,等待时间到了继续执行。

(9)Looper死循环为什么不会导致应用卡死?

卡死就是ANR,产生的原因有2个:

1、在5s内没有响应输入的事件(例如按键,触摸等),

2、BroadcastReceiver在10s内没有执行完毕。

事实上我们所有的Activity和Service都是运行在loop()函数中,以消息的方式存在,所以在没有消息产生的时候,looper会被block(阻塞),主线程会进入休眠,一旦有输入事件或者Looper添加消息的操作后主线程就会被唤醒,从而对事件进行响应,所以不会导致ANR

简单来说looper的阻塞表明没有事件输入,而ANR是由于有事件没响应导致,所以looper的死循环并不会导致应用卡死。

四、Handler流程分析

Message 创建流程分析

Android13 Message创建流程分析-CSDN博客

Handler 创建流程分析

Android13 Handler创建流程分析-CSDN博客

Handler post流程分析

Android13 Handler post流程分析-CSDN博客

Handler sendMessage流程分析

Android13 Handler sendMessage流程分析-CSDN博客

Looper prepare流程分析

Android13 Looper prepare流程分析-CSDN博客

Looper Looper流程分析

Android13 Looper Looper流程分析-CSDN博客​​​​​​​

  • 12
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值