首先需要搞清楚android消息机制的原理
大部分人应该都明白,简单过一下。
- 在线程中有looper
- looper.loop方法,通过死循环来遍历消息队列(MessageQueue),没有消息的话,线程睡眠,避免cpu压力过大(cpu空转也是非常耗电的)
- 有消息的话,通过handler.sendMesage 方法,在looper的消息队列中插入一条消息(Message),同时唤醒线程。(注意哈,这个线程是通过其他线程唤醒的,他自己已经睡眠了)
来上代码实现一下
首先是Message
class Message(
var what: Int,
var obj: Object? = null,
var runnable: Runnable? = null
) {
// 标记了是从哪个handler发出的
lateinit var target: Handler
}
Handler
open class Handler(private val looper: Looper = Looper.myLooper()!!) {
/**
* 处理消息
*/
open fun handleMessage(message: Message) {
if (message.runnable != null) {
message.runnable!!.run()
}
}
/**
* 发送消息
*/
fun send(message: Message) {
message.target = this
looper.messageQueue.enqueue(message)
}
}
消息队列
class MessageQueue {
// 消息队列
private val queue: Deque<Message> = LinkedList<Message>()
private var quit = false
// 线程锁
private val lock = Object()
fun enqueue(message: Message) {
val empty = queue.isEmpty()
queue.add(message)
// 如果之前消息队列,为空,则唤醒线程
if (empty) {
synchronized(lock) {
println("有新消息,已经解锁")
lock.notifyAll()
}
}
}
fun next(): Message? {
if (queue.isEmpty()) {
if (quit) {
println("已退出")
return null
}
println("消息队列为空,已经锁定")
synchronized(lock) {
lock.wait()
}
}
return queue.poll()
}
fun quit() {
quit = true
}
}
Looper
这个稍微复杂一点
class Looper private constructor() {
// 消息队列
var messageQueue = MessageQueue()
fun quit() {
messageQueue.quit()
}
companion object {
// 使用ThreadLocal 保存所有的Looper
private val looperThreadLocal = ThreadLocal<Looper>()
// 主线程的looper,也就是第一个初始化的
private var mainLooper: Looper? = null
@JvmStatic
fun myLooper(): Looper? {
return looperThreadLocal.get()
}
@JvmStatic
fun mainLooper(): Looper {
return mainLooper!!
}
@JvmStatic
fun prepare() {
if (looperThreadLocal.get() == null) {
looperThreadLocal.set(Looper())
} else {
throw IllegalStateException("looper exists in ${Thread.currentThread().name} thread")
}
}
@JvmStatic
fun loop() {
val myLooper: Looper = myLooper() ?: throw IllegalStateException("looper not exists")
while (true) {
// 没有消息,是会睡眠的哟
val message = myLooper.messageQueue.next() ?: break
// 注意这个target是谁
message.target.handleMessage(message)
}
}
@JvmStatic
fun prepareMainLooper() {
if (mainLooper == null) {
mainLooper = Looper()
looperThreadLocal.set(mainLooper)
} else {
throw IllegalStateException("looper exists in main thread")
}
}
}
}
测试一下
fun main(args: Array<String>) {
Looper.prepareMainLooper()
val handler = Handler()
Thread {
println("请输入内容(输入q退出)")
val scanner = Scanner(System.`in`)
var quit = false
while (scanner.hasNextLine() && !quit) {
val line = scanner.nextLine()
println("${Thread.currentThread().name} 输入的:$line")
handler.send(Message(0, runnable = Runnable {
println("${Thread.currentThread().name} 接收到:$line")
}))
when (line) {
"q" -> {
Looper.mainLooper().quit()
quit = true
}
}
}
}.also {
it.name = "子线程"
it.start()
}
Looper.loop()
}
image.png
此demo有一点与android的不同,在没有消息时,android使用linux内核epoll机制 实现线程睡眠,而本demo使用的是线程锁
demo传送门 https://github.com/pokercc/message-demo
请注意工程结构基于idea,不是android studio
网友评论