Android消息机制详解

2019-05-30  本文已影响0人  漫步_蜗牛

use for

相信于此,绝大多数同学都会回答消息机制是android 为了线程间通信而引入的工具。可以轻松的将一个任务切换到handler所在线程执行。android开发规范有规定,不允许于子线程更新ui,这样会触发异常;我们平时使用handler主要都是将子线程切换到主线程中去执行;因此从本质上来来说,Handler并不是专门用于更新UI的,它只是常被开发者用来更新UI。


Q?为何不能在主线程外更新ui呢?

A: 因为Android的UI线程是非线程安全的,应用更新UI,是调用invalidate()方法来实现界面的重绘,而invalidate()方法是非线程安全的,也就是说当我们在非UI线程来更新UI时,可能会有其他的线程或UI线程也在更新UI,这就会导致界面更新的不同步。因此我们不能在非UI主线程中做更新UI的操作。也就是说我们在使用Android中的线程时,要保证: 更新ui都在UI主线程执行.

Q:那为何不将需要更新ui的操作放在UI线程执行呢?

A: 我们都知道在java中,线程存在以下几种基本状态:创建,就绪运行阻塞,死亡。我们的应用启动后,所有的交互都是在UI线程完成的;如果在UI执行延时操作,如常见的网络请求UI线程就会进入阻塞状态;此时用户就无法响应任何操作了;如果此过程超过5秒,就会让程序处于ANR(application not response),这时用户就可能想要和你的应用说声gg了。

Q:Android提供了哪几种线程间通信方式?

A: AsyncTask?,Handler 。为什么AsynTask打了个?呢,我们可以简单看下AsynTask源码,他内部也是接住handler来进行线程间通信的。

Q:MessageQueue存在Targer对象的消息,那和我们正常流程中,由handler传递的消息有什么出入呢?
A: 其实平时我们使用的Message,都是通过Handler发送的,有一些系统消息,他们会直接通过调用MessageQueue发送一个屏障消息,这类消息没有Target,然后配合Handler发送异步消息来使用;当MessageQueue读取到屏障消息后,他们会直接在链表中找到最近的异步消息,直接执行。


feature-要素

desc

  1. handler创建前,Looper.loop()执行前;需要保证当前线程Looper有创建,而这个保证即Looper.prepare();主线程由于在1ActivityThread1创建时,已经做过,所以无需执行;
  2. Looper.loop()中有一个死循环,所以线程资源不会释放;在线程运行结束时调用MessageQueue中的quit函数,我们才能释放资源;
  3. Java中,所有非静态成员变量会持有当前对象的引用(不然你又是怎么引用外部类的各种成员变量和函数等);那样我们在Activity中通过new Handler() , 创建的对象会持有当前页面的引用;而我们发送的每个消息不能保证是立即执行,以及迅速执行结束的,handler.sendEmptyMessageDelayed;消息是会持有handler做为他的target,那在这个message在通过msg.target.dispatchMessage(msg);会一直被持有;这样会导致messageQueue->message->handler->activity|fragment;在页面被销毁,声明周期执行到desatory时,activity不会得到释放,从而内存泄漏handler得到消息处理时,如果当前页面已经被销毁,执行Ui更新,又会导致难以预料的问题。
  4. 针对3所提的我们可以按以下两种处理:
    1:页面destory销毁时,调用handler.removeCallbacksAndMessages(null);
    2:通过软引用创建静态Handler对象;

流程解析

android handler流程分析晚上有很多资料;我们这儿简单介绍下:

对于一个消息创建流程,加入消息队列,MessageQueue简单通过Mq代表了:

image.png

接下来是消息读取的流程图:

image.png

上面流程图中有涉及到一个新的消息概念屏障消息(无target的消息)

我们向消息队列MessageQueue发送一个屏障消息,然后再发送一个异步消息;在我们读取到这个屏障消息的时候,我们会找到链表后的第一个异步消息;这样就能快速执行该异步消息了;

系统有一个postSyncBarrier()用来发送屏障消息,但是被隐藏了;我们可以反射调用或者直接向MessageQueue表头反射插入一个Message,但是不建议这样做;

发送异步消息可以通过:创建Handler时,传入异步参数:

public Handler(boolean async);
public Handler(Callback callback, boolean async);
public Handler(Looper looper, Callback callback, boolean async);

这样就能发送屏障消息和异步消息了;

在系统源码ViewRootImpl.scheduleTraversals中,为了更快响应UI刷新事件时

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        //设置同步障碍,确保mTraversalRunnable优先被执行
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //内部通过Handler发送了一个异步消息
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }

mTraversalRunnable调用了performTraversals执行measure、layout、draw

为了让mTraversalRunnable尽快被执行,在发消息之前调用MessageQueue.postSyncBarrier设置了同步屏障

上一篇下一篇

猜你喜欢

热点阅读