在Android应用程序中,存在一个主线程我们通常叫做UI线程,可以进行界面的更新等,进行系统的消息发送。接触Android久了,就会知道,Activity的生命周期就是通过系统内部的Handler发送消息来进行回调,其中消息传递过程是Handler的消息机制;

为什么要使用Handler的消息机制?
—它的设计避免了多线程的并发执行操作,我们知道Android规定:UI只是支持单线程模型。
假如多个线程都更新UI的界面,就会发生线程不安全问题,造成数据的丢失等等;
有了Handler之后一些好事的的操作都可以放到其他子线程里面执行,执行完毕之后可通过Handler与UI主 线程进行交互,这个过程很好的避免的多线程的操作,同时增强了用户的体验。

下图是消息机制总体流程图:

它主要涉及下面四个类 Handler、MessageQueue、Message、Looper;
Message:消息
MessageQueue:消息队列,负责存储消息,它内部其实是一个单链表的结构
Handler:负责消息的发送和处理.必须要关联当前线程中唯一的Looper对象,才可以,否则抛异常
Looper:负责消息队列中消息的轮询操作,每一个线程只能有唯一的Looper对象(比如UI线程)
在UI线程被创建的时候,就会初始化一个Looper对象(是唯一的),然后在通过Looper.looper()方法(这个方法是一个无限循环方法),发现MessageQueue中有消息的时候,就会把它取出发送给Handler的handleMessage()方法处理;
对于消息队列,当有消息的时候,它会被唤醒,来处理消息,当没有消息的时候,他就会处于阻塞状态。
消息基本传递过程大体是这样的:
1.Handler发送一个Message(消息)同时这个消息会插入MessageQueue(消息队列)中;
2.然后Looper会轮询这个消息队列,发现有消息,会取出这个消息;
3.取出来之后会把它传递给原来的Handler对象处理。

通过上面我们会有一个整体认识,下面通过源代码来进行剖析:
最开始UI主线程被创建的时候,在ActivityThread内部会创建一个唯一的Looper对象:

   public static void main(String[] args) {          //代码省略...        Process.setArgV0("<pre-initialized>");        Looper.prepareMainLooper();//创建消息循环Looper        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        AsyncTask.init();          //代码省略...        Looper.loop();//执行循环消息        throw new RuntimeException("Main thread loop unexpectedly exited");    } 

上面有注释的两块地方,开始了对Looper的操作处理—
第一个方法Looper.prepareMainLooper()是初始化一个Looper对象和一个消息队列
第二个方法Looper.loop()是Looper对象对消息队列进行轮询操作,发现里面有消息的时候就会发给Handler处理,没有消息的时候就Looper对象本身就会处于阻塞状态
再接着看一下Looper.prepareMainLooper()方法的源代码:

    public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();        }    }      public static void prepare() {        prepare(true);    }    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));    }

从这里我们可以看到,这个方法内部首先调用了prepare(false),
prepare(false)内部它先调用了ThreadLocal的get()方法,来进行判断当前UI线程中是否有Looper对象变量值,如果有的话,get方法返回就不等于null,那么这里就会抛一个异常“only one Looper may be created per thread ”意思就是每个线程只允许创建一个Looper对象,如果Looper对象已经有了那么就不能再调用这个方法了,否则会抛上面异常。这里因为是第一次启动创建,所以一定是null,不会报异常。
再往下面执行的时候调用 sThreadLocal.set(new Looper(quitAllowed));
ThreadLocal会把新new 的Looper对象设置进去,下次调用sThreadLocal.get()的时候可以取出的取出这个变量的值,当然前提是在同一个线程中(这里是UI线程)。熟悉线程的朋友,知道这个ThreadLocal类作用就是存储与当前线程有关的变量,不涉及其他线程!
执行到最后的时候,会调用Looper.myLooper()将looper对象赋值给到Looper类里面的sMainLooper变量。

 public static  Looper myLooper() {        return sThreadLocal.get();//再当前线程中通过ThreadLocal取出我们设置的Looper对象    }

通过new Looper(quitAllowed)我们看一下Looper的带参构造方法,就会发现原来消息队列MessageQueue被Looper内部创建了.

 private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);//Looper封装了消息队列        mThread = Thread.currentThread();    }

到这里,我们就会发现Android的UI线程中会有一个默认的Looper对象(而且是唯一的),它里面还封装了一个消息队列。这样Looper和MessageQueue就关联上了
还有一个Looper.loop()方法,我们到后面提到再说…

下面我们在看看Handler是如何与Looper和MessageQueue关联上的呢?
当我们在UI主线程中创建一个Handler对象的时候(比如在一个Actvity中创建 Handler mHandler=new Handler();)其实在这个过程中会把主线程里面的Looper和MessageQueue分别赋值给到Handler里面的对应变量,这个看一下 Handler的构造方发就会发现

 public Handler() {        this(null, false);    } public Handler(Callback callback, boolean async) {          //省略次要代码...        mLooper = Looper.myLooper();//这里赋值Looper        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        mQueue = mLooper.mQueue;//这里赋值MessageQueue        mCallback = callback;        mAsynchronous = async;    }

1,mLooper = Looper.myLooper();//这里赋值Looper,当前是在UI主线程里面,会把当前线程中唯一的一个Looper传递过来(上面已经分析了);
同理mQueue = mLooper.mQueue;//这里赋值MessageQueue,也会把与Looper关联的唯一MessageQueue传递进来;
到这里也就是说我们创建的Handler对象里面包含了主线程的Looper和MessageQueue,这样Handler就和它们关联上了;

当使用Handler对象发送消息的时候会调sendMessage();

 public final boolean sendMessage(Message msg)    {        return sendMessageDelayed(msg, 0);    } 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);    }

发送消息的方法内部调用了发送延迟消息的方法,从内部知道是从开机到现在时间的上延迟了0秒,接着把这个时间当做是消息发送时间,接着调定时发送消息的方法,这个方法里面会先拿出消息队列,后面又会把这条消息插入消息队列里面。这一点从以上代码看还是比较容易理解的。接着上面代码往下继续看代码enqueueMessage(queue, msg, uptimeMillis)

 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;//这个this不就是当前的Handler        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

msg.target = this,把Handler对象赋给了Message中的target引用,这样这条消息中的target指向Handler.
queue.enqueueMessage(msg, uptimeMillis)就是消息队列自己插入消息的方法了。
这条条消息Message里面的target(Handler类型)引用指向了我们最开始创建的Hanlder对象
看看消息队列MessageQueue的queue.enqueueMessage(msg, uptimeMillis)是如何插入消息的:

 boolean enqueueMessage(Message msg, long when) {         //省略次要代码...        synchronized (this) {            msg.markInUse();            msg.when = when;            Message p = mMessages;            boolean needWake;            if (p == null || when == 0 || when < p.when) {                // New head, wake up the event queue if blocked.                msg.next = p;                mMessages = msg;                needWake = mBlocked;            } else {                needWake = mBlocked && p.target == null &&msg.isAsynchronous();                Message prev;                for (;;) {                    prev = p;                    p = p.next;                    if (p == null || when < p.when) {                        break;                    }                    if (needWake && p.isAsynchronous()) {                        needWake = false;                    }                }                msg.next = p; // invariant: p == prev.next                prev.next = msg;            }            // We can assume mPtr != 0 because mQuitting is false.            if (needWake) {                nativeWake(mPtr);            }        }        return true;    }

代码比较长,其实也没有什么,就不全部贴出来了,只看重要的部分,用心看,你会明白,MessageQueue内部存储其实使用的单链表存储结构,它并不是一个什么队列容器,(而是一个消息内部包含另一个消息的应用,通过这个应用可以查找到下一个消息),通过这个方法可以将一个消息插入到消息的队尾里去,这样一条Message消息就成功插入到里面去了。

好了,前面的过程我们都了解的差不多了,我来看看最重要的一个方法 Looper.loop(),
上面已经说了,一旦消息队列俩面有消息,Looper就会轮询出来发送给Handler处理。

    public static void loop() {        final Looper me = myLooper();//拿到当前UI线程中唯一的Looper对象        if (me == null) {            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }        final MessageQueue queue = me.mQueue;//拿到当前UI线程中唯一MesssageQueue对象       //省略次要代码...        for (;;) {//看到了吗是一个无限循环        //阻塞的方法,没有消息的时候,MessageQueue会阻塞在这里            Message msg = queue.next(); //might block            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }              //省略次要代码...            msg.target.dispatchMessage(msg);//这条msg.target指向我们创建的Handler             //省略次要代码..            msg.recycle();        }    }

在这个方法里面先是取得前线程中的Looper对象,然后拿到消息队列,之后进入到for循环,这个for循环是一个死循环,第一行代码是注释可以知道,会阻塞,其实是一旦没有消息的时候就会休眠阻塞,一旦消息来了就会唤醒处理,这个next()方法里面也是消息队列链表的取出和删除操作,大家可以自己去读原码。这里取出刚才那条Message之后会调用 msg.target.dispatchMessage(msg)方法,msg.target指向的是放送这条消息的 Hanlder对象!呵呵,继续往下看看Handler类里面这个代码吧

 public void dispatchMessage(Message msg) {        if (msg.callback != null) {//判断Message自身Callback接口是否为null            handleCallback(msg);        } else {//到了Handler来处理消息了            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }

别的不说了,就看看handleMessage(msg),这个是Handler的接口回调方法。我们最熟悉的吧。

最后总结一下:
Handler负责消息的放送和处理,Looper负责轮询消息队列中的消息。消息队列负责消息的插入和取出删除。

更多相关文章

  1. [置顶] Android之高仿手机QQ聊天
  2. 每天学习一个Android中的常用框架——12.Handler
  3. Android之高仿手机QQ聊天
  4. Android,谁动了我的内存(1)
  5. Android多线程AsyncTask详解
  6. android Handler
  7. 在Linux下安装Android(安卓)SDK
  8. Android之线程阻塞(一)
  9. Android中的网络时间同步 !!!!!!!!

随机推荐

  1. 基于Gradle支持Android(安卓)Studio的蒲
  2. Android资源管理框架(Asset Manager)简要介
  3. Android清除数据、清除缓存、一键清理的
  4. Android(安卓)性能优化 内存优化 How to
  5. Android(安卓)SQLiteException: near "":
  6. android之生命周期onSaveInstanceState()
  7. HTC HERO/Android(安卓)刷机+中文配置
  8. Android用户也能幸福爆棚
  9. 【翻译】在 Android(安卓)真机上运行 App
  10. Android(安卓)layout 使用include和merge