当前位置: 移动技术网 > IT编程>移动开发>Android > Android 消息机制详解及实例代码

Android 消息机制详解及实例代码

2019年07月24日  | 移动技术网IT编程  | 我要评论

3D预测彩酷酷caikuku,龙汉荣,假婚姻现象多发

android 消息机制

1.概述

android应用启动时,会默认有一个主线程(ui线程),在这个线程中会关联一个消息队列(messagequeue),所有的操作都会被封装成消息队列然后交给主线程处理。为了保证主线程不会退出,会将消息队列的操作放在一个死循环中,程序就相当于一直执行死循环,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数(handlermessage),执行完成一个消息后则继续循环,若消息队列为空,线程则会阻塞等待。因此不会退出。如下图所示:

handler 、 looper 、message有啥关系?

在子线程中完成耗时操作,很多情况下需要更新ui,最常用的就是通过handler将一个消息post到ui线程中,然后再在handler的handlermessage方法中进行处理。而每个handler都会关联一个消息队列(messagequeue),looper负责的就是创建一个messagequeue,而每个looper又会关联一个线程(looper通过threadlocal封装)。默认情况下,messagequeue只有一个,即主线程的消息队列。

上面就是android消息机制的基本原理,如果想了解更详细,我们从源码开始看。

2.源码解读

(1)activitythread主线程中启动启动消息循环looper

public final class activitythread {
  public static void main(string[] args) {
    //代码省略
    //1.创建消息循环的looper
    looper.preparemainlooper();

    activitythread thread = new activitythread();
    thread.attach(false);
    if (smainthreadhandler == null) {
      smainthreadhandler = thread.gethandler();
    }
    asynctask.init();

    //2.执行消息循环
    looper.loop();
    throw new runtimeexception("main thread loop unexpectedly exited");
  }
}

activitythread通过looper.preparemainlooper()创建主线程的消息队列,最后执行looper.loop()来启动消息队列。handler关联消息队列和线程。

(2)handler关联消息队列和线程

public handler(callback callback, boolean async) {
    //代码省略
    //获取looper
    mlooper = looper.mylooper();
    if (mlooper == null) {
      throw new runtimeexception(
        "can't create handler inside thread that has not called looper.prepare()");
    }
    //获取消息队列
    mqueue = mlooper.mqueue;
  }

handler会在内部通过looper.getlooper()方法来获取looper对象,并且与之关联,并获取消息队列。那么looper.getlooper()如何工作的呢?

  public static @nullable looper mylooper() {
    return sthreadlocal.get();
  }

  public static @nonnull messagequeue myqueue() {
    return mylooper().mqueue;
  }
  public static void prepare() {
    prepare(true);
  }
  //为当前线程设置一个looper
  private static void prepare(boolean quitallowed) {
    if (sthreadlocal.get() != null) {
      throw new runtimeexception("only one looper may be created per thread");
    }
    sthreadlocal.set(new looper(quitallowed));
  }
  //设置ui线程的looper
  public static void preparemainlooper() {
    prepare(false);
    synchronized (looper.class) {
      if (smainlooper != null) {
        throw new illegalstateexception("the main looper has already been prepared.");
      }
      smainlooper = mylooper();
    }
  }

在looper类中,mylooper()方法,通过sthreadlocal.get()来获取的,在preparemainlooper()中调用prepare()方法,在这个方法中创建了一个looper对象,并将对象设置了sthreadlocal()。这样队列就和线程关联起来了。通过sthreadlocal.get()方法,保证不同的线程不能访问对方的消息队列。

为什么要更新ui的handler必须在主线程中创建?

因为handler要与主线程的消息队列关联上,这样handlermessage才会执行在ui线程,此时ui线程才是安全的。

(3)消息循环,消息处理

消息循环的建立就是通过looper.loop()方法。源代码如下:

/**
   * run the message queue in this thread. be sure to call
   * {@link #quit()} to end the loop.
   */
  public static void loop() {
    final looper me = mylooper();
    if (me == null) {
      throw new runtimeexception("no looper; looper.prepare() wasn't called on this thread.");
    }
    //1.获取消息队列
    final messagequeue queue = me.mqueue;
    //2.死循环,即消息循环
    for (;;) {
      //3.获取消息,可能阻塞
      message msg = queue.next(); // might block
      if (msg == null) {
        // no message indicates that the message queue is quitting.
        return;
      }
      //4.处理消息
      msg.target.dispatchmessage(msg);
      //回收消息
      msg.recycleunchecked();
    }
  }

从上述程序我们可以看出,loop()方法的实质上是建立一个死循环,然后通过从消息队列中逐个取出消息,最后处理消息。对于looper:通过looper.prepare()来创建looper对象(消息队列封装在looper对象中),并且保存在sthreadlocal中,然后通过通过looper.loop()进行消息循环,这两步通常成对出现。

public final class message implements parcelable {
  //target处理
  handler target; 
  //runnable类型的callback
  runnable callback;
  //下一条消息,消息队列是链式存储的
  message next;
}

从源码中可以看出,target是handler类型。实际上就是转了一圈,通过handler发送消息给消息队列,消息队列又将消息分发给handler处理。在handle类中:

//消息处理函数,子类覆写
public void handlemessage(message msg) {
}

private static void handlecallback(message message) {
    message.callback.run();
  }

//分发消息
public void dispatchmessage(message msg) {
    if (msg.callback != null) {
      handlecallback(msg);
    } else {
      if (mcallback != null) {
        if (mcallback.handlemessage(msg)) {
          return;
        }
      }
      handlemessage(msg);
    }
  }

从上述程序可以看出,dispatchmessage只是一个分发的方法,如果run nable类型的callback为空,则执行handlemessage来处理消息,该方法为空,我们会将更新ui的代码写在该函数中;如果callback不为空,则执行handlecallback来处理,该方法会调用callback的run方法。其实这是handler分发的两种类型,比如post(runnable callback)则callback就不为空,当我们使用handler来sendmessage时通常不设置callback,因此,执行handlermessage。

 public final boolean post(runnable r)
  {
    return sendmessagedelayed(getpostmessage(r), 0);
  }

  public string getmessagename(message message) {
    if (message.callback != null) {
      return message.callback.getclass().getname();
    }
    return "0x" + integer.tohexstring(message.what);
  }

  public final boolean sendmessagedelayed(message msg, long delaymillis)
  {
    if (delaymillis < 0) {
      delaymillis = 0;
    }
    return sendmessageattime(msg, systemclock.uptimemillis() + delaymillis);

public boolean sendmessageattime(message msg, long uptimemillis) {
    messagequeue queue = mqueue;
    if (queue == null) {
      runtimeexception e = new runtimeexception(
          this + " sendmessageattime() called with no mqueue");
      log.w("looper", e.getmessage(), e);
      return false;
    }
    return enqueuemessage(queue, msg, uptimemillis);
  }

从上述程序可以看到,在post(runnable r)时,会将runnable包装成message对象,并且将runnable对象设置给message对象的callback,最后会将该对象插入消息队列。sendmessage也是类似实现:

public final boolean sendmessage(message msg)
  {
    return sendmessagedelayed(msg, 0);
  }

不管是post一个runnable还是message,都会调用sendmessagedelayed(msg, time)方法。handler最终将消息追加到messagequeue中,而looper不断地从messagequeue中读取消息,并且调用handler的dispatchmessage分发消息,这样消息就源源不断地被产生、添加到messagequeue、被handler处理,android应用就运转起来了。

3.检验

new thread(){
  handler handler = null;
  public void run () {
    handler = new handler();
  };
}.start();

上述代码有问题吗?

looper对象是threadlocal的,即每个线程都用自己的looper,这个looper可以为空。但是,当在子线程中创建handler对象时,如果looper为空,那么会出现异常。

public handler(callback callback, boolean async) {
    //代码省略
    //获取looper
    mlooper = looper.mylooper();
    if (mlooper == null) {
      throw new runtimeexception(
        "can't create handler inside thread that has not called looper.prepare()");
    }
    //获取消息队列
    mqueue = mlooper.mqueue;
  }

当mlooper为空时,抛出异常。这是因为looper对象没有创建,因此,sthreadlocal.get()会返回null。handler的基本原理就是要与messagequeue建立关联,并且将消息投递给messagequeue,如果没有messagequeue,则handler没有存在的必要,而messagequeue又被封住在looper中,因此创建handler时,looper一定不能为空。解决办法如下:

new thread(){
  handler handler = null;
  public void run () {
    //为当前线程创建looper,并且绑定到threadlocal中
    looper.prepare()
    handler = new handler();
    //启动消息循环
    looper.loop();
  };
}.start();

如果只创建looper不启动消息循环,虽然不抛出异常,但是通过handler来post或者sendmessage()也不会有效。因为虽然消息会被追加到消息队列,但是并没有启动消息循环,也就不会从消息队列中获取消息并且执行了。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网