kotlin实现android 消息机制

首先需要搞清楚android消息机制的原理

大部分人应该都明白,简单过一下。

  1. 在线程中有looper
  2. looper.loop方法,通过死循环来遍历消息队列(MessageQueue),没有消息的话,线程睡眠,避免cpu压力过大(cpu空转也是非常耗电的)
  3. 有消息的话,通过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()

}

《kotlin实现android 消息机制》 image.png

此demo有一点与android的不同,在没有消息时,android使用linux内核epoll机制 实现线程睡眠,而本demo使用的是线程锁

demo传送门 https://github.com/pokercc/message-demo
请注意工程结构基于idea,不是android studio

    原文作者:pokercc
    原文地址: https://www.jianshu.com/p/e95b4a4a147a
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞