开发者

Android消息机制原理深入分析

开发者 https://www.devze.com 2022-12-10 10:27 出处:网络 作者: Hdnw
目录1.消息机制原理的解释2.android的消息机制概述Handler的背景(三个常见问题)Handler的工作原理的解释3.消息机制的分析1.了解Message2.了解Handler3.消息队列的工作原理4.Looper的工作原理4.Handler使用(DEMO)
目录
  • 1.消息机制原理的解释
  • 2.android的消息机制概述
    • Handler的背景(三个常见问题)
    • Handler的工作原理的解释
  • 3.消息机制的分析
    • 1.了解Message
    • 2.了解Handler
    • 3.消息队列的工作原理
    • 4.Looper的工作原理
  • 4.Handler使用(DEMO)

    1.消息机制原理的解释

    在主线程里创建一个Handler,然后在分线程中引用这个Handler来发送Message对象给MessageQueue,循环器Looper从MessageQueue里面取出一个需要处理的Message,交给Handler处理,一般是进行UI处理。处理完之后,Meswww.devze.comsage就没有太大的用处,Looper清理Message,让Message回到默认状态。

    Android消息机制原理深入分析

    2.Android的消息机制概述

    Handler的背景(三个常见问题)

    (1)Android为什么要提供Handler?

    这是因为Android规定访问UI只能在主线程中进行,如果在子线程中访问UI,那么程序就会抛出异常,但是Android又建议不要在主线程中进行耗时操作,否则会导致线程无法响应即ANR,比如我们需要从服务器拉取一些信息并将其显示在UI上,这个时候必须在子线程中进行拉取工作,拉取完毕后,不能在子线程上直接访问UI,这时候通过Handler就可以将访问UI的操作切换到主线程去执行。

    (2)系统为什么不允许在子线程中访问UI呢?

    这个因为Android的UI控件并不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态。

    (3)为什么系统编程不对UI控件的访问加上锁机制呢?

    缺点有两个:

    • 加上锁机制会让UI访问的逻辑变得复杂
    • 锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行

    鉴于这两个缺点,最简单且高效的方法就是采用单线程模型来处理UI操作。

    Handler的工作原理的解释

    子线程默认没有Looper的,如果需要使用Handler,那么在Handler创建时就需要为线程创建Looper,利用Looper来构建内部的消息循环系统,如果当前线程没有Looper,那么就会报错。Handler创建完毕之后,Handler通过post方法把一个Runnable传到Looper中去处理,或者通过send方法发送消息,Looper会调用MessageQueue的enqueueMessage方法将消息发入消息队列中,然后Looper不断循环发现需要处理的消息之后,就会调用消息中的Runnable或者或者Handler的handleMessage方法,这样一来,Handler中的业务逻辑就被切除到创建Handler所在的线程中去执行。

    Android消息机制原理深入分析

    3.消息机制的分析

    1.了解Message

    可理解为线程间通讯的数据单元,可通过message携带需要的数据

       创建Message的方法

    val message:Message= Message()
    val message1:Message=Message.obtain()//它利用了Message中消息池(sPool)

       封装数据:

    int what:标识

    int arg1:保存int数据

    int arg2:保存int数据

    Object obj:保存任意时刻的数据

    Long when :记录应该被处理的时间值,若为即时消息,时间值=发送时间,若为延时消息,时间值=发送时间+延迟时间

    Handler target:用来处理消息的Handler对象,就是发送消息的Handler

    Runnable callback:用来处理消息的回调器

    Message next:指向下一个Message用来形成一个链表

    Message sPool:用来缓存处理过的Message,以便复用

    2.了解Handler

    Handler是Message的处理器,同时也负责消息的发送和移除的工作

    (1)Handler的构造方法

    Android API 30以上,使用Handler()方法时会显示删除线,并提示相关的方法已经被弃用,不建议使用。如图所示:

    Android消息机制原理深入分析

    但是安卓不是弃用Handler这个类,而只是弃用Handler的两个构造方法:

    Handler()
    Handler(callback:Handler.Callback)

     安卓建议采用如下方法来解决

    1.使用Executor

    2.明确指定Looper

    3.使用Looper.getMainLooper()定位并使用主线程的Looper

    4.如果又想在其他线程,又想要不出bug,请使用Handler(looper:Looper)或者Handler(Looper.myLooper())这两个构造方法。

    (2) Handler导致的内存泄漏问题

    在Android中最常用的一种内存泄漏是Handler导致的泄漏,原因:

    (1)在Activity被摧毁时,延迟消息还没发出,Handler可能有未执行完或者正在执行的Message,MessagesQue开发者_Go学习ue就会持有这个消息的引用,导致Handler持有Activity的引用,进而导致GC无法回收Activity。

    (2)Handler中有还没执行完的Message,还在运行,而运行中的子线程不会被回收,所以就导致了内存泄漏。

    解决方法:

    1.在Activity的onDestroy()方法中,清空Handler中的未执行或者正在执行的Message和Callbacks

    override fun onDestroy() {
            super.onDestroy()
            handler.removeCallbacksAndMessages(null)
        }

    2.static+弱引用

    class  MyHandler(activity: HandlerActivity):Handler(Looper.getMainLooper()){
            private val myWeakReference:WeakReference<HandlerActivity> = WeakReference(activity)
            override fun handleMessage(msg: Message) {//处理消息的回调方法
                myWeakReference.get()?.run {
                ...
            }
          }
     }

    (3)Handler的常用方法:

    • 发送即时消息:sendMessage(msg  Message)
    • 发送延时消息:sendMessageDelayed(msg Message,delayMillis Long)
    • 处理方法:handleMessage(msg Message)(回调方法)
    • 移除还未处理的消息:removeMessages(what int)

    部分源码:

    由下面的源码可以看出,这些方法的本质是调用了queue.enqueueMessage(msg,uptimeMillis)方法。

    sendMessage(Message msg)
        ->sendMessageDelayed(msg,0)
    sendEmptyMessage(int what)
        ->sendEmptyMessageDelayed(what,0)
    sendEmptyMessageDelayed(what,0)//发送不带数据的消息
         ->sendMessageDelayed(msg,delayMillis)
    sendMessageDelayed(Message msg,long delayMillis)
         ->sendMessageAtTime(msg,SystemClock.uptimeMillis()+delayMillis)
    sendMessageAtTime(Message msg,long uptimeMillis)
         -&gwww.devze.comt;enaueueMessage(queue,msg,uptimeMillis)//将消息添加到消息队列中
    enaueueMessage(MessageQueue queue,Message msg,long uptimeMillis)
         ->queue.enqueueMessage(msg,uptimeMillis)//调用消息队列保存消息对象
    removeMessage(int what)//移除消息
         ->mQueue.removeMessage(this,what,null)//调用消息队列移除它内部的指定what消息
    handleMessage(Message msg)//处理消息的回调方法

    3.消息队列的工作原理

    MessageQueue消息队列,负责入队和出队,储存Handler发送的消息,它是一个按Message的when的排序的优先队列。
    虽然MessageQueue叫消息队列,但是它内部是用链表来实现的。

    MessageQueue主要包含两个操作:插入和读取,读取操作本身会伴随删除操作,插入和读取对应的方法分别为enqueueMessage和next:

    boolean enqueueMessage(Message msg, long when):往消息队列中插入一条消息

    Message next() :消息队列中取出一条消息并将其从消息队列中移除

    enqueueMessage和 next方法的部分源码:

    enqueueMessage的主要操作就是单链表的插入操作

    boolean enqueueMessage(Message msg, long when) {//将Messages插入消息队列
        ...
       msg.when = when;//指定消息应该被处理的时间
       ...
       for (;;) {//将当前消息对象保存到消息队列中的一个合适的位置
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needwake && p.isAsynchronous()) {
                     needWake = false;
                 }
        }//最终的结果是:消息队列是按when来排序的
        ...
        nativeWake(mPtr);//通过本地方法实现对处理等待状态的底层线程
        ...
    }

     next方法是一个无限循环的方法。如果消息队列中没有消息,那么next方法可以阻塞在这里,

    当有新的消息到来时,next方法会返回这条消息并将其中单链表中移除。

    Message next() {//取出一个合适的Message对象,可能不会立刻返回
        ...
        nativePollOnce(ptr, nextPollTimeoutMillis);//本地方法,会导致可能处理等待状态,但不会阻塞主线程
        ...
        Message msg = mMessages;//取出消息队列中的第python一个消息
        ...
        return msg;//返回
        ...
    }

    4.Looper的工作原理

    Looper在Android的消息机制中扮演着消息循环的角色。

    Looper为一个线程开启一个消息循环,创建MessageQueue,负责循环取出Message Queue里面的当前需要处理的Message,也就是说,它会一直不停地从MessageQueue中查看是否会有新消息,如果有新消息就会交给对应的Handler进行处理,处理完后,将Message缓存到消息池中以备复用,否则就一直阻塞在那里,Looper退出后,Handler发送消息会失败,线程会立刻终止。

    常用方法:​

    Looper.prepare():为当前线程创建一个Looper。

    Looper.loop():开启消息循环.

    Looper.getMainLooper():获取主线程的Looper。

    Looper.quit()直接推迟Looper

    Looper.quitSafely()设定一个退出标记,然后把消息队列中的已有消息处理完毕才安全地退出。

    如何为一个线程创建Looper?

    thread {
                Looper.prepare()
                val  handler:Handler=MyHandler(this)
                Looper.loop()
            }

     loop方法的部分源码:

     public static void loop() {
        final Looper me = myLooper();//得到looper对象
        ...
        for (;;) {//无限循环
             ...
             Message msg = me.mQueue.next(); // 从消息队列中取出消息
             ...
             msg.target.dispatchMessage(msg);//调用Handler去分发并处理消息
             ...
             msg.recycleUnchecked();//回收利用Message
             ...
        }
    }

    4.Handler使用(DEMO)

    功能描述:

    1.初始时

    显示10,可以通过点击按钮改变其值

    2.点击自动增加

    每隔一秒上面的文本数值增加1,但最大显示20并作出提示

    3.点击自动减少

    每隔一秒上面的文本数值减少1,但是最小显示1并作出提示

    4.点击暂停

    上面的数值文本不再变化

    效果图:

    Android消息机制原理深入分析

    代码如下:

    class HandlerActivity : AppCompatActivity() {
        lateinit var handler: Handler
        lateinit var number:TextView
        lateinit var increase:Button
        lateinit var decrease:Button
        lateinit var pause:Button
        //static+弱引用
        class  MyHandler(activity: HandlerActivity):Handler(Looper.getMainLooper()){
            private val myWeakReference:WeakReference<HandlerActivity> = WeakReference(activity)
            override fun handleMessage(msg: Message) {//处理消息的回调方法
                myWeakReference.get()?.run {
                    var numberint:Int=Integer.parseInt(number.text.toString())
                    when(msg.what){
                        1->{
                            if(numberint==20){
                                Toast.makeText(this,"已经达到最大值",Toast.LENGTH_SHORT).show()
                                 return
                            }
                            numberint++
                            number.text= numberint.toString()
                            handler.sendEmptyMessageDelayed(1,1000)
                        }
                        2->{
                            if(numberint==1){
                                Toast.makeText(this,"已经达到最小值",Toast.LENGTH_SHORT).show()
                                return
                            }
                            numberint--
                            number.text=numberint.toString()
                            handler.sendEmptyMessageDelayed(2,1000)
                        }
                        else -> {}
                    }
                }
     编程客栈       }
        }
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_handler)
            number=findViewById(R.id.number)
            increase=findViewById(R.id.increase)
            decrease=findViewById(R.id.decrease)
            pause=findViewById(R.id.pause)
            handler=MyHandler(this)
            increase.setOnClickListener {
                increase.isEnabled=false
                decrease.isEnabled=true
                pause.isEnabled=true
                handler.removeMessages(2)
                handler.sendEmptyMessage(1)
            }
            decrease.setOnClickListener {
                increase.isEnabled=true
                decrease.isEnabled=false
                pause.isEnabled=true
                handler.removeMessages(1)
                handler.sendEmptyMessage(2)
            }
            pause.setOnClickListener {
                increase.isEnabled=true
                decrease.isEnabled=true
                pause.isEnabled=false
                handler.removeMessages(1)
                handler.removeMessages(2)
            }
        }
        override fun onDestroy() {
            super.onDestroy()
            handler.removeCallbacksAndMessages(null)
        }
    }

    到此这篇关于Android消息机制原理深入分析的文章就介绍到这了,更多相关Android消息机制内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号