美文网首页Android开发经验谈
kotlin实现android 消息机制

kotlin实现android 消息机制

作者: Xigong | 来源:发表于2019-02-28 23:07 被阅读9次

    首先需要搞清楚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()
    
    }
    
    image.png

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

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

    相关文章

      网友评论

        本文标题:kotlin实现android 消息机制

        本文链接:https://www.haomeiwen.com/subject/wdvluqtx.html