侧边栏壁纸
博主头像
Eoser's page! 博主等级

@学习@生活@自己

  • 累计撰写 114 篇文章
  • 累计创建 29 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

笔记:Android异步消息(Handler)处理机制

eoser
2023-04-04 / 0 评论 / 0 点赞 / 4 阅读 / 0 字
  • Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃。

  • Android子线程中创建Handler,需要在new Handler前Looper.prepare() ,否则会崩溃。

  • 每个线程只能Looper.prepare() 一次,每个线程中最多只会有一个Looper对象,否则会报错。

  • 没Looper.prepare() 报错的原因是Handler的构造函数中调用了Looper.myLooper()获取Looper,而Looper的值在Looper.prepare() 时设置,没有设置Looper在new Handler中获取的为null,所以报错。

  • 主线程中的Handler也没有调用Looper.prepare()方法不报错的原因是,在程序启动的时候,系统已经帮我们自动调用了Looper.prepare()方法。

    //启动程序主线程,关注Looper.prepareMainLooper()
    public static void main(String[] args) {
    SamplingProfilerIntegration.start();
    CloseGuard.setEnabled(false);
    Environment.initForCurrentUser();
    EventLogger.setReporter(new EventLoggingReporter());
    Process.setArgV0("");
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    AsyncTask.init();
    if (false) {
        Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    //Looper.prepareMainLooper()中prepare()与setMainLooper
    public static final void prepareMainLooper() {
    prepare();
    setMainLooper(myLooper());
    if (Process.supportsProcesses()) {
        myLooper().mQueue.mQuitAllowed = false;
    }
    }
  • Handler传递消息用的Message对象,最终是通过传入Handler的sendMessageAtTime(Message msg, long uptimeMillis)方法来传递的;在该方法中,msg会被加入MessageQueue 消息队列,调用的是enqueueMessage方法

  • MessageQueue并没有使用一个集合把所有的消息都保存起来,它只使用了一个mMessages对象表示当前待处理的消息

  • MessageQueue所谓的入队其实就是将所有的消息按时间来进行排序,这个时间当然就是我们刚才介绍的uptimeMillis参数。具体的操作方法就根据时间的顺序调用msg.next,从而为每一个消息指定它的下一个消息是什么,我理解为相当于数据结构中的链表结构

    final boolean enqueueMessage(Message msg, long when) {
    if (msg.when != 0) {
        throw new AndroidRuntimeException(msg + " This message is already in use.");
    }
    if (msg.target == null && !mQuitAllowed) {
        throw new RuntimeException("Main thread not allowed to quit");
    }
    synchronized (this) {
        if (mQuiting) {
            RuntimeException e = new RuntimeException(msg.target + " sending message to a Handler on a dead thread");
            Log.w("MessageQueue", e.getMessage(), e);
            return false;
        } else if (msg.target == null) {
            mQuiting = true;
        }
        msg.when = when;
        Message p = mMessages;
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            this.notify();
        } else {
            Message prev = null;
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
            msg.next = prev.next;
            prev.next = msg;
            this.notify();
        }
    }
    return true;
    }
  • 在loop()方法中,通过调用的MessageQueue的next()方法让消息出队列,该方法会取出当前Message,并且将方法中的mMessages替换为下一条消息。

  • 在loop()方法中如果没有下一条Message,会阻塞,直到有新的消息入队列。

  • 最终的回调函数handleMessage能取得Message也是在next()方法后,回调传入的,在之前的出入队列时,Handler对象也是被保存在Message.target中的,所以才能回调。

  • 以下是盗的图解: 632893385bcf819b68518d1b5fd65582.png

  • Looper主要作用: 1、 与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。 2、 loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。

  • Handler的处理: 1、首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。 2、Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。 3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue相关联。 4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。 5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。

  • 笔记阅读/参考内容: CSDN博主「鸿洋_」的文章 CSDN博主「guolin_」的文章

0

评论区