Android消息机制源码分析

在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发送消息Message的时候,消息的内部持有Handler引用targer,而target会指向当前的Handler对象,而这个Handler对象又是关联了当前线程(这里是主线程)的Looper对象和消息队列MessageQueue。
而Looper是为了重复不断从消息队列里面取出消息…
当消息被从消息对列取出的时候,会让消息Message内部持有Handler引用target指向的Handler对象处理;
这样正好是Handler自己发送和自己处理消息。
注意这里Handler是关联的主线程的Looper;所以无论是哪里的消息,都会在主线程里面处理;
当然我们自己可以让Handler关联子线程这种的Looper;所以无论是哪里的消息,都会在这个子线程里面处理;比如下面代码;

	new Thread() {
			@Override
			public void run() {
				Looper.prepare();
				Handler mThreadHandler = new Handler(Looper.myLooper());
				Looper.loop();
			}
		}.start();
/**或者下面这样都可以,为什么可以自己看看源码分析有助于加深对消息机制的理解*/
		HandlerThread thread = new HandlerThread("myHandlerThread");
		thread.start();
		Handler mHandler = new Handler(thread.getLooper());

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

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值