Handler的实现原理及其与Message、MessageQueue关系详解

       在Android中为我们提供了一个主线程和子线程之间的通信机制,这种机制就是利用Handler、Message、Looper和MessageQueue来彼此联系起来的。那么为什么要提供这么一种机制呢?因为Android是单线程模式的,所谓单线程模式就是说,在app启动时就会创建一个对应的唯一的一个主线程(ActivityThread实例),这个实例维护着界面ui的更新、用户的各种响应事件等。这些更新只能放在主线程中执行(典型的例子就是在Framework中会检查主线程中是否有网络请求的操作,如果有就会直接抛出异常),主线程并不安全,并且耗时的操作必须放在子线程中去执行,这里我不再讨论线程不安全、耗时操作不能放在主线程中执行的原因,前面的文章介绍过。基于这个原因,Framework引入了这么一种主线程和子线程通信的机制,Handler、Message、Looper和MessageQueue彼此协作完成通信。

第一部分  主线程与子线程之间的通信实例

       本文分两部分介绍,第一部分先展示如何使用这种机制来完成主线程和子线程的通信,第二部分介绍其实现的原理源码分析。

1、常规用法,在子线程中直接使用handler.sendMessage()方法

	       //创建Handler对象,并重写其处理方法
               mUIhandler = new UIHandler() {
			@Override
			public void handleMessage(Message msg) {
				// 处理请求后的任务
			}
		};
		// 第一种方式
		new Thread(new Runnable() {
			@Override
			public void run() {
				// 这里处理耗时、网络操作,
				// 下面处理完后发送消息给Handler来处理
				Message msg = mUIhandler.obtainMessage();
				msg.what = 0;
				// msg.obj = value;
				mUIhandler.sendMessage(msg);
			}
		}).start();
        上面的使用方法很简单,就是先创建一个Handler对象,然后重写其handleMessage()方法,接下来开启子线程,并执行run()中的请求业务,之后再获取Message对象,然后发送出去给Handler的handleMessage()处理。

2.指定时间点发送消息

		long tm = SystemClock.uptimeMillis();
		// 第二种方式,指定时间点开始执行
		mUIhandler.postAtTime(new Runnable() {
			@Override
			public void run() {
				Message msg = mUIhandler.obtainMessage();
				msg.what = 1;
				mUIhandler.sendMessage(msg);
			}

		}, tm + 15000);
       Handler提供了一个postAtTime()来处理业务,在使用时需要传递一个Runnable对象,在该对象的run()中执行相应的业务,同上,执行完后再发送消息给Handler的handleMessage()来处理,他的第二个参数是指定在某时间点来post到MessageQueue,这个参数需要根据SystemClock.uptimeMillis()方法来计算,这个方法返回的是系统开启启动的总时间然后我们想要它在开机后的多长时间启动就加上那个时间就可以了。此外,Handler还提供了一个postDelayed()方法,此方法也是两个参数,第一个是Runnable对象,第二个是指定多少毫秒以后再执行,跟postAtTime()在使用上有点区别,但是本质上没有区别,因为postDelayed()的实现还是使用了SystemClock.uptimeMillis()+delayMillis来完成的。第二部分介绍原理时会说道。

3.使用Handler的Callback完成消息处理

		final Handler callBackHandler = new Handler(new Handler.Callback() {
			@Override
			public boolean handleMessage(Message msg) {
				String str = mContentTv.getText().toString();
				mContentTv.setText(str + "\n\n第三种方式,使用Callback实现消息处理");
				return false;
			}
		});
		callBackHandler.post(new Runnable() {
			@Override
			public void run() {
				Message msg = callBackHandler.obtainMessage();
				msg.what = 1;
				callBackHandler.sendMessage(msg);
			}
		});
       如上代码所示,我们在创建Handler对象时并没有直接重写其handleMessage()方法,而是在创建时传递了一个Callback对象,重写Callback的handleMessage()方法来完成消息处理。有一点不同的是Callback的handleMessage()方法有一个boolean返回值,而Handler的handleMessage()没有boolean返回值,所以前者更灵活一些,而且我们也可以使用Callback来灵活实现项目的“金字塔”分层的设计。因此,我们可以在不用继承Handler的基础上利用Handler.Callback来实现消息传递与处理,如果我们要实现一个Handler的子类,那就必须重写Handler的handleMessage()方法了。

      4.在子线程中创建Handler对象。

       记得我刚开始做Android开发入门在使用Handler时,总是牢记不能在主线程以外的地方创建使用Handler,然而,事实上我们可以在子线程中创建Handler,只需要在创建Handler的时候将主线程的Looper传递过去就行,如下所示:

new Thread(new Runnable() {
			@Override
			public void run() {
				//获取主线程的Looper
				Looper looper = Looper.getMainLooper();
				Handler handler = new Handler(looper){
					@Override
					public void handleMessage(Message msg){
						//这里消息业务
					}
				};
				
			}
		}).start();

    当然我们也可以不用这么麻烦去自己使用Looper来创建Handler对象,可以直接使用HandlerThread来帮我们完成。 关于HandlerThrad的用法详见http://blog.csdn.net/u012481172/article/details/44243681; 第23条。

    先调用Looper的getMainLooper()获取主线程的Looper,然后在创建Handler的时候再传递过去即可,这样Handler在sendMessage的时候就会将消息传递给主线程了。

    以上我们介绍了Handler的几种简单用法,但是如果是新手的话肯定会不明所以,糊里糊涂的,所以下面就会介绍Handler的原理。

第二部分 主线程和子线程的通信原理

      要使用Android的Handler进行主/子线程通信需要以下步骤:

      1、在线程中调用Looper.prepare()准备一个Looper。

      2、在线程中调用Looper.loop()进入消息循环队列。

      3、使用handler发送消息。

		
		new Thread(new Runnable(){
			@Override
			public void run() {
				//第一步,准备Looper
				Looper.prepare();
				Handler handler = new Handler(){
					@Override
					public void handleMessage(Message msg){
						//处理消息业务
					}
				};
				//第二步,进入消息循环
				Looper.loop();
				//发送消息
				handler.sendEmptyMessage(1);
				
			}
			
		}).start();
      使用的步骤已经说明,现在说下其原理:

    首先调用Looper的prepare()方法准备一个Looper对象将其放入ThreadLocal中:

    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");
        }
      //new一个Looper对象,并放到ThreadLocal中
      sThreadLocal.set(new Looper(quitAllowed));
    }
      可以看到,在创建一个Looper对象后就将该对象放到ThreadLocal中去,在调用myLooper()时获取该Looper对象,我们再看看Looper的构造器做了哪些事情呢?
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mRun = true;
        mThread = Thread.currentThread();
    }

      首先创建一个MessageQueue对象mQueue,在进行消息循环时(就是执行looper()方法)会不断从mQueue中读取消息。然后又调用Thread.currentThread()获取当前程序所在的线程。通常如果我们直接在UI线程中创建Handler时,这个mThread就是一个主线程,如果是在子线程中创建Handler并创建消息队列时,此mThread就是当前的子线程。以下是Looper.prepare()的工作流图:

   

      在Looper.prepare()完成后就可以调用loop()进入消息循环了,我们看下loop()的源码:

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        //上面说到,直接调用myLooper()获取到在prepare()中创建的Looper对象,
        //在myLooper()中只有一行代码:mThreadLocal.get()。
        final Looper me = myLooper();
       //如果没有Looper对象就抛出异常:prepare()没有在这个线程中调用。
       if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
       //获取到mQueue对象,这个对象就是在Looper中创建的,也即在prepare()或prepareMainLooper()中创建
       final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        //进入无限循环
        for (;;) {
           //取出Message,这个地方有可能会出现阻塞情况,因为next()方法会有产生阻塞;如果为null就退出,
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
           //Message的targer变量是一个Handler实例,当从MessageQueue中取出Message后就调用Message的一个
           //Handler的dispatchMessage()方法
            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycle();
        }
    }

     这里放一张我手绘的Looper.loop()的工作流图:

    

    上图中用红线连接起来的三个msg对象表示的是相同的实例。 

    looper()方法的逻辑很简单,基本都在注释上面说明了,这里再梳理一遍:首先获取到Looper对象,然后取出该Looper对象的MessageQueue对象,并进入无限循环,不断的调用MessageQueue的next()方法读取Message,如果没有就直接退出,如果有就取出Message的Handler并执行该Handler的dispatchMessage()方法。那么我们再去看一下Handler的dispatchMessage()方法是什么鬼:

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
       //判断Message的callback是否为空,如果不为空就执行handleCallback(msg),Message的callback变量就是一个Runnable
       //如果该Runnable不为空就调用handleCallback(msg),里面就一行代码:msg.callback.start();
       if (msg.callback != null) {
            handleCallback(msg);
        } else {
          //如果mCallback不为空就执行Callback的handleMessage。
          if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
        //执行Handler的handleMessage()
        handleMessage(msg);
        }
    }
      可以看到,在dispatchMessage()中最终调用了我们重写的handleMessage()方法。在dispatchMessage()方法中一系列的调用有一个优先级,即:首先如果Message的callback不为空就去执行,并且下面的Callback和Handler的handleMessage()都不会执行,如果Message的callback为空,就去判断Handler的Callback是否为空,不为空就去执行,如果执行成功就直接return掉,如果执行失败再去执行。总结一句就是:

       ① 如果Message的callback(即Runnable)部位null那么Callback和Handler的handleMessage()都不会执行;

       ② 如果Message的callback是null且Callback不为nul,就去执行Callback的handleMessage(),如果Callback的handleMessage()执行成功就不会执行Handler的handleMessage(), 如果执行失败就会执行Handler的handleMessage();

       ③ 如果Callback也是null,那么就直接去执行Handler的handleMessage()方法。

        所以显然,我们最常用的直接重写Handler的handleMessage()方法的优先级是最低的。

       那么上面所说的Message的callback是在什么时候被赋值的呢?换句话说我们在什么情况下会使用到呢?答案就是在获取Message对象的时候用到,请看下面的例子:

		Handler handler = new Handler(new Handler.Callback() {
			@Override
			public boolean handleMessage(Message msg) {
				
				return false;
			}
		});
		//获取一个Message对象
		Message msg = Message.obtain(handler, new Runnable(){
			@Override
			public void run() {
				//执行耗时操作
			}
			
		});
      如上所示使用Message的callback的场合,就是调用Message.obtain(handler,runnable)方法,该方法会将handler对象赋值给target变量,将runnable赋值给callback变量,target在Looper的loop()方法中会使用到,而callback会在Handler的dispatchMessage()中使用到。实际上当我们使用了上面的方式来获取一个Message时,最终的结果就是handler调用了它的dispatchMessage()然后导致了runnable的执行,这两个参数是有因果关系的,有了handler才会导致runnable执行。

      我们最常用的是使用Handler的obtainMessage()来获取一个Message对象的,查看其源码可以知道,实际上Handler的obtainMessage()也是调用了Message的obtainMessage(this)方法的,其中this表示当前的Handler。只不过用此种方式获取Message不会有callback创建。

      经过上面的分析我们知道了Looper是如何工作的,也知道了Handler的handlerMessage等方法是何时被调用的,也初步接触到MessageQueue,那么我们是如何将消息发送给MessageQueue的呢?接下来就将以下使用Handler发送消息的几种机制,我们从创建Handler说起。我们在使用Handler时一定会直接new一个Handler,它只提供一个public的构造器,因此只能通过new的方式创建,那么我们去看看他的构造器做了哪些事情:

    

    /**
     * Default constructor associates this handler with the queue for the
     * current thread.
     *
     * If there isn't one, this handler won't be able to receive messages.
     */
    public Handler() {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
        //获取Looper,上面说过myLooper实际上就是把在prepare()中set到ThreadLocal中的Looper对象get出来。
        mLooper = Looper.myLooper();
        //如果mLooper为空,就说明我们没有调用prepare()
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //然后将Looper的mQueue赋值给Handler的mQueue,这个mQueue在发送消息时会用到。mLooper的mQueue也是在prepare()
        //的时候创建的
        mQueue = mLooper.mQueue;
        mCallback = null;
    }

       创建完Handler后我们就需要获取Message对象了,关于获取Message对象,上面已经说过了,就不在赘述。接下来就是发送消息了。

      使用Handler发送消息(Message)可以使用两个系列的方法实现,第一就是post(...)系列,第二就是sendMessage(...)系列。

      1.post(...)系列:

     (1)post(Runnable):似乎看起来没有使用到Message,但其实不然,因为在post(Runnable)方法中系统会帮我们创建一个Message对象,并且把Runnable对象赋值给该Message的callback,所以,如果使用了post(Runnable)方法,那么Handler的handleMessage()和Callback的handleMessage()都将无效。

     (2)postAtTime(Runnable,miultTime):指定某时间点再去执行,原理同post(Runnable)一样。

              ....

      2.sendMessage(...)系列:

     (1)sendMessage(msg):就是单纯的发送一个Message。

              ....

              ....

       不管是什么方法发送消息,最后都会调用到 sendMessageAtTime(Message msg, long uptimeMillis)方法:

    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            msg.target = this;
            sent = queue.enqueueMessage(msg, uptimeMillis);
        }
        else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
    }
        第一个参数就是一个Message对象,第二个参数就是延迟执行的时间,如果我们在使用时没有使用到这个参数就表示立刻去执行。可以看到,在该方法中,首先将当前的Handler对象赋值给Message的target变量,随后调用mQueue的enqueueMessage(msg,uptimeMillis)方法。需要注意这个mQueue变量是我们在创建Handler对象的时候从Looper中获取的,从根本上来讲的就是Looper调用prepare()的时候创建的。然后我们再去看看MessageQueue的enqueueMessage()方法做了哪些事情:

    final boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg + " This message is already in use.");
        }
        if (msg.target == null) {
            throw new AndroidRuntimeException("Message must have a target.");
        }

        boolean needWake;
        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;
            }

            msg.when = when;
            Message p = mMessages;
            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 {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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;
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }

         初步的调用流程就介绍到这里,在这里并没有更深层次的分析native层的使用,仅仅很肤浅的介绍了下其工作原理,最后总结一下其调用流程:

      首先Looper调用prepare()方法准备好一个MessageQueue,当然,一个app开启后主线程自己就创建了一个MessageQueue,在ActivityThread线程(就是主线程)中,Framework已经调用了Looper的prepare()并进入了loop()。当prepare()准备好了之后就调用loop()方法进入消息循环,当然第一次是没有消息的,因此会处于阻塞状态(在MessageQueue的next()方法处),如果有消息就读取一个Message,然后调用Message的dispatchMessage(),然后根据优先级以此调用Message的callback(一个Runnable对象)、Handler.Callback的handleMessage()、Handler的handleMessage()。另一方面,当我们要发送一个消息时,通常会调用sendMessage()或postMessage()等方法,本质上都是调用了sendMessageAtTime()方法,消息发送成功后会唤醒looper()继续执行。然后就调用到了dispatchMessage()了。

         最后,关于Looper我们通常不会用到,除非你需要自己维护一个消息队列,而不使用主线程的消息队列,关于Message和Handler还有很多有用的方法可以使用,具体的自己一看便知了。最后放几张手绘的流程图!


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值