Android中Handler的消息机制分析(二)

貂蝉
• 阅读 890

ps:阅读原文可获取 demo 源码,文章末尾有原文链接

ps:源码是基于 android api 27 来分析的,demo 是用 kotlin 语言写的。

上一篇Android中Handler的消息机制分析(一)主要分析的是 Handler 讲消息的发送和 Handler 将消息的处理,这一篇分析的是 Handler 其他常用的一些知识点。

我们来看看 Handler 的 post 方法,它也是属于发送消息的方法;

public final boolean post(Runnable r)
{

   return  sendMessageDelayed(getPostMessage(r), 0);

}

从 post 方法可以看出,它调用了 Handler 的 sendMessageDelayed 方法,从Android中Handler的消息机制分析(一)这篇文章可以知道,Handler 的 sendMessageDelayed 方法最终调用了 Handler 的 enqueueMessage 方法,enqueueMessage 方法调用了 MessageQueue 的 enqueueMessage 方法,MessageQueue 的 enqueueMessage 方法最终完成 Handler 的消息发送(本质上是将消息插入到链表中);所以 Handler 的 postDelayed、sendMessage 等发送消息的方法最终调用 Handler 的 enqueueMessage 方法。

我们来看一下 Handler 的所有构造方法;

//1、
public Handler() {
    this(null, false);
}

//2、
public Handler(Handler.Callback callback) {
    this(callback, false);
}

//3、
public Handler(Looper looper) {
    this(looper, null, false);
}

//4、
public Handler(Looper looper, Handler.Callback callback) {
    this(looper, callback, false);
}

//5、
public Handler(boolean async) {
    this(null, async);
}

//6、
public Handler(Handler.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();
    if (mLooper == null) {
        throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

//7、
public Handler(Looper looper, Handler.Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

我们来看注释7 的构造方法中的参数,因为这个构造方法的参数包含注释2 到注释6 构造方法的参数;looper 参数表示一个轮循器,looper 调用 loop 方法使得线程不断循环让 Handler 处理消息,looper 初始化和looper 调用 loop 方法以及 Handler 的初始化一定是在同一线程的,Handler 的初始化后一定会有 looper;callback 参数表示当 Handler 不重写 handleMessage 方法时,callback 接口的 handleMessage 方法就会代替 Handler 处理消息,具体用法可以看Android中Handler的消息机制分析(一)这篇文章;async 参数表示是否为异步消息。

1、这里就有一个疑问了,Handler 的初始化后一定会有 looper,但是我们一般在 Activity 中初始化 Handler 的时候调用的是 Handler 无参构造方法,looper 是空的啊?

首先 ActivityThread 的 main 方法是先比 Activity 启动先执行并执行在主线程的,也进行了 Looper 初始化和调用 Looper.loop 方法对线程进程循环;

public static void main(String[] args) {

    ......
    Looper.prepareMainLooper();
    ......
    Looper.loop();
    ......

}

Looper的prepareMainLooper 方法最终会调用 Looper 的 prepare(boolean quitAllowed) 方法;

private static void prepare(boolean quitAllowed) {

    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    
    //8、
    sThreadLocal.set(new Looper(quitAllowed));

}

这时候注释8 的代码保存的是主线程的 Looper。

我们在 Activity 中初始化一个 Handler 时,Activity 是运行在主线程的,所以 Handler 的初始化也是运行在主线程的,我们看看 Handler 的无参构造方法;

public Handler() {
    this(null, false);
}
public Handler(Handler.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());
        }
    }

    //9、
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

Handler 的无参构造方法调用 Handler(Handler.Callback callback, boolean async) 构造方法,并获取一个 Looper,我们看看注释9 的代码,也就是 Looper 的 myLooper 方法;

public static @Nullable Looper myLooper() {

    return sThreadLocal.get();

}

由于 Handler 的无参构造方法是运行在主线程的,sThreadLocal.get 方法拿到的是主线程的 Looper,所以我们一般在 Activity 中初始化 Handler 的时候调用的是 Handler 无参构造方法,looper 不是空的。

我们在回顾注释6 的 Handler 的构造方法中的以下代码;

if (mLooper == null) {

throw new RuntimeException(
    "Can't create handler inside thread that has not called Looper.prepare()");

}

2、这就说明在 Handler 处理消息的线程进行 Looper 初始化。

2、1 演示一下在子线程初始化 Handler 时没有初始化 Looper 对象,看看报错结果。

inner class MyC : Handler.Callback {

    override fun handleMessage(msg: Message?): Boolean {
        return true
    }

}
var myC: MyC = MyC()
class MyT: Thread() {

    override fun run() {
        super.run()
        var h: Handler = Handler(myC)
    }
}
var myT: MyT = MyT()
 myT.start()

运行程序时,报以下错误

java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-4,5,main] that has not called Looper.prepare()

                                                                   at android.os.Handler.<init>(Handler.java:205)
                                                                   at android.os.Handler.<init>(Handler.java:132)
                                                                   at com.xe.handlerdemo2.MainActivity$MyT.run(MainActivity.kt:39)

3、这里有疑问,一个线程中有多少个 Looper?用多个 Handler 在同一个线程发送消息不会导致多个 Handler 处理消息混乱吗?

3、1我们先来解惑一个线程中有多少个 Looper

我们来看看 Looper 的 prepare(boolean quitAllowed) 方法,它是对 Looper 进行初始化;

private static void prepare(boolean quitAllowed) {

    
    //10、
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));

}

从注释10 的代码可以看出,当一个线程先初始化 Looper 之前,先从 sThreadLocal 变量取出值看看是否为空,如果不为空就会抛出异常,所以一个线程只能初始化一次 Looper。

3、2 我们再解惑用多个 Handler 在同一个线程发送消息会不会导致多个 Handler 处理消息混乱

首先我们用 Handler 发送消息过程中,会调用 Handler 的 enqueueMessage 方法;

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

    //11、
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);

}

我们再看获取消息并分发消息的 Looper.loop 方法;

public static void loop() {

    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;
    ......
    for (;;) {
        Message msg = queue.next(); // might block
        ......
        try {

            //12、
            msg.target.dispatchMessage(msg);
            ......
        } finally {
            ......
        }
        ......
    }

}

我们从注释11 可以看出,发送消息的时候 Message 的 target 属性持有 Handler,从注释12 可以看出,分发消息的时候用 Message 的 target 属性去分发,所以同一个线程中用不同的 Handler 去发送消息的时候,不会造成 Handler 之间处理消息混乱,即哪个 Handler 发送的消息,哪个 Handler 就会相应的处理;下面我们用代码演示一下;

var mH2: Handler? = null
var mH3: Handler? = null
companion object {
    var HANDLE_MESSAGE: Int = 1
    var EXIT_THREAD: Int = 2
}
inner class MyH2(looper: Looper): Handler(looper) {
    override fun handleMessage(msg: Message?) {
        super.handleMessage(msg)
        when(msg?.what) {
            HANDLE_MESSAGE -> {
                var s: String = msg.obj as String
                Log.d("MainActivity","--MyH2--" + s)
            }
            EXIT_THREAD -> {
                Looper.myLooper().quitSafely()
            }
        }
    }
}
inner class MyH3(looper: Looper): Handler(looper) {
    override fun handleMessage(msg: Message?) {
        super.handleMessage(msg)
        when(msg?.what) {
            HANDLE_MESSAGE -> {
                var s: String = msg.obj as String
                Log.d("MainActivity","--MyH3--" + s)
            }
            EXIT_THREAD -> {
                Looper.myLooper().quitSafely()
            }
        }
    }
}

inner class MyT2: Thread() {
    override fun run() {
        super.run()
        Looper.prepare()
        mH2 = MyH2(Looper.myLooper())
        Looper.loop()
    }
}
inner class MyT3: Thread() {
    override fun run() {
        super.run()
        Looper.prepare()
        mH3 = MyH3(Looper.myLooper())
        Looper.loop()
    }
}
inner class MyT4: Thread() {
    override fun run() {
        super.run()
        var m2: Message = Message.obtain();
        m2.what = HANDLE_MESSAGE
        m2.obj = "222"
        var m3: Message = Message.obtain();
        m3.what = HANDLE_MESSAGE
        m3.obj = "333"
        mH2?.sendMessage(m2)
        mH3?.sendMessage(m3)
    }
}
MyT2().start()
MyT3().start()
MyT4().start()

程序运行后,日志打印如下所示:

09-25 15:14:45.179 18159-18288/com.xe.handlerdemo2 D/MainActivity: --MyH2--222
09-25 15:14:45.179 18159-18289/com.xe.handlerdemo2 D/MainActivity: --MyH3--333

当我们不再使用线程时,要终止消息循环,Looper 提供了2个终止消息循环的方法,如下所示;

public void quit() {
    mQueue.quit(false);
}

public void quitSafely() {
    mQueue.quit(true);
}

quit 方法会把 MessageQueue 消息池中全部的消息全部清空,不管是延迟消息还是非延迟消息;quitSafely 方法仅仅会清空 MessageQueue 消息池中全部的延迟消息,并将消息池中全部的非延迟消息派发出去让 Handler 去处理。

4、这里有最后一个疑问,主线程的死循环一直运行是不是特别消耗CPU资源?死循环导致卡死?

在主线程的 MessageQueue 没有消息时,主线程便阻塞在 loop 的 MessageQueue.next 方法中的 nativePollOnce 方法里,相当于 java 层的线程 waite 机制,此时主线程会释放 CPU 资源进入休眠状态,直到下个消息到达时调用 nativewake,很多时候主线程处于休眠状态,并不会大量消耗的 CPU 资源;首先主线程中的死循环不会导致卡死,它会使得主线程阻塞在 MessageQueue 的 next 中的 nativePollOnce 方法里,当有消息来时就会唤醒主线程进行消息处理,即使主线程在休眠的时候也有其他的线程在处理事件,死循环里面有一个阻塞,阻塞并不等于卡死,死循环和卡死并不是同一个概念。

点赞
收藏
评论区
推荐文章
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
元旦 元旦
4年前
每日一问(一)Handler相关知识
1、Handler负责发送Message和处理Mesage2、Message就是消息载体,可用what区分,也可传递对象3、MessageQueue消息队列,存储Message4、Looper循环取出MessageQueue里的Message交给Handler处理。5、一个线程只有一个Looper和MessageQueue,子线程中使用Handler一
全靠这份Android知识点PDF大全,月薪30K
第一阶段:Android基础知识回顾:回顾Android开发编程,深入理解Android系统原理和层次结构,深入分析Handler源码和原理;回顾Java,C/C,Kotlin、dart在Android开发中必用的语言,熟悉一下几种语言混淆后的特性;回顾AndroidIPC和JNI的底层原理和热更新技术回顾Native开发要点,使用C结
九路 九路
4年前
Android HandlerThread源码解析
在上一章Handler源码解析文章中,我们知道App的主线程通过Handler机制完成了一个线程的消息循环。那么我们自己也可以新建一个线程,在线程里面创建一个Looper,完成消息循环,可以做一些定时的任务或者写日志的功能。这就是HandlerThread的作用AndroidHandler消息机制源码解析(https://www.cnblogs.co
九路 九路
4年前
Android Handler消息机制源码解析
好记性不如烂笔头,今天来分析一下Handler的源码实现Handler机制是Android系统的基础,是多线程之间切换的基础。下面我们分析一下Handler的源码实现。Handler消息机制有4个类合作完成,分别是Handler,MessageQueue,Looper,MessageHandler:获取消息,发送消息,以及处理消息的类Mes
Stella981 Stella981
3年前
Android中AsyncTask的使用
原文https://blog.csdn.net/liuhe688/article/details/6532519在Android中实现异步任务机制有两种方式,Handler和AsyncTask。Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细
Stella981 Stella981
3年前
Android的消息处理机制(图+源码分析)——Looper,Handler,Message
作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想。android源码中包含了大量的设计模式,除此以外,androidsdk还精心为我们设计了各种helper类,对于和我一样渴望水平得到进阶的人来说,都太值得一读了。这不,前几天为了了解android的消息处理机制,我看了Looper,Handler,
Stella981 Stella981
3年前
Android中的常见通信机制和Linux中的通信机制
HandlerHandler是Android系统中的一种消息传递机制,起作用是应对多线程场景。将A进程的消息传递给B线程,实现异步消息处理。很多情况是将工作线程中需要更新UI的操作消息传递给UI主线程,而实现更新UI操作。因为工作线程和主线程是共享地址空间,即Handler实例对象mHandler位于线程间共享的内存堆上,工作线程和主线
Wesley13 Wesley13
3年前
03.Android崩溃Crash库之ExceptionHandler分析
目录总结00.异常处理几个常用api01.UncaughtExceptionHandler02.Java线程处理异常分析03.Android中线程处理异常分析04.为何使用setDefaultUncaughtExceptionHandler前沿上一篇整体介绍了crash崩溃
Stella981 Stella981
3年前
Handler更新UI的几种方式
Handler、loop、MessageQueue的工作原理Message:Handler接收和处理的消息对象Looper:每个线程只能拥有一个looper.它的loop方法负责读取MessageQueue中的消息,读到信息之后就把消息返回给handler处理MessageQueue:消息队列。程序创建Looper对象时,会在它的构造器中创建
Stella981 Stella981
3年前
Android的消息循环与Handler机制理解
一、概念1、事件驱动型什么是事件驱动?就是有事了才去处理,没事就躺着不动。假如把用户点击按钮,滑动页面等这些都看作事件,事件产生后程序就执行相应的处理方法,就是属于事件驱动型。2、消息循环把需要处理的事件表示成一个消息,并且把这个消息放入一个队列。消息循环就是一循环,for或者while都一样。从消息队列里面取出未处理的消息,然后调用该消息的