关于android:Android中Handler的消息机制分析二

4次阅读

共计 7143 个字符,预计需要花费 18 分钟才能阅读完成。

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 办法里,当有音讯来时就会唤醒主线程进行音讯解决,即便主线程在休眠的时候也有其余的线程在处理事件,死循环外面有一个阻塞,阻塞并不等于卡死,死循环和卡死并不是同一个概念。

正文完
 0