zoukankan      html  css  js  c++  java
  • Android 异步消息处理机制 让你在深入了解 Looper、Handler、Message之间的关系

    转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自【张鸿洋的博客】

    非常多人面试肯定都被问到过,请问Android中的Looper , Handler , Message有什么关系?本篇博客目的首先为大家从源代码角度介绍3者关系。然后给出一个easy记忆的结论。

    1、 概述

    Handler 、 Looper 、Message 这三者都与Android异步消息处理线程相关的概念。那么什么叫异步消息处理线程呢?
    异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息。然后回调对应的消息处理函数。运行完毕一个消息后则继续循环。若消息队列为空,线程则会堵塞等待。


    说了这一堆,那么和Handler 、 Looper 、Message有啥关系?事实上Looper负责的就是创建一个MessageQueue。然后进入一个无限循环体不断从该MessageQueue中读取消息,而消息的创建者就是一个或多个Handler 。

    2、 源代码解析

    1、Looper

    对于Looper主要是prepare()和loop()两个方法。
    首先看prepare()方法
    public static final void prepare() {
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            sThreadLocal.set(new Looper(true));
    }
    

    sThreadLocal是一个ThreadLocal对象。能够在一个线程中存储变量。能够看到。在第5行。将一个Looper的实例放入了ThreadLocal,而且2-4行推断了sThreadLocal是否为null。否则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同一时候也保证了一个线程中仅仅有一个Looper实例~相信有些哥们一定遇到这个错误。
    以下看Looper的构造方法:
    private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mRun = true;
            mThread = Thread.currentThread();
    }
    
    在构造方法中,创建了一个MessageQueue(消息队列)。
    然后我们看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.");
            }
            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 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);
                }
    
                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();
            }
    }
    

    第2行:
    public static Looper myLooper() {
    return sThreadLocal.get();
    }
    方法直接返回了sThreadLocal存储的Looper实例。假设me为null则抛出异常。也就是说looper方法必须在prepare方法之后运行。
    第6行:拿到该looper实例中的mQueue(消息队列)
    13到45行:就进入了我们所说的无限循环。
    14行:取出一条消息。假设没有消息则堵塞。


    27行:使用调用 msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。

    Msg的target是什么呢?事实上就是handler对象。以下会进行分析。
    44行:释放消息占领的资源。

    Looper主要作用:
    1、 与当前线程绑定,保证一个线程仅仅会有一个Looper实例。同一时候一个Looper实例也仅仅有一个MessageQueue。
    2、 loop()方法。不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。
    好了。我们的异步消息处理线程已经有了消息队列(MessageQueue),也有了在无限循环体中取出消息的哥们。如今缺的就是发送消息的对象了,于是乎:Handler登场了。

    2、Handler

    使用Handler之前,我们都是初始化一个实例。比方用于更新UI线程。我们会在声明的时候直接初始化,或者在onCreate中初始化Handler实例。

    所以我们首先看Handler的构造方法,看其怎样与MessageQueue联系上的,它在子线程中发送的消息(一般发送消息都在非UI线程)怎么发送到MessageQueue中的。


    public Handler() {
            this(null, false);
    }
    public Handler(Callback callback, boolean async) {
            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()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }


    14行:通过Looper.myLooper()获取了当前线程保存的Looper实例,然后在19行又获取了这个Looper实例中保存的MessageQueue(消息队列),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。

    然后看我们最经常使用的sendMessage方法

       public final boolean sendMessage(Message msg)
        {
            return sendMessageDelayed(msg, 0);
        }

       public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
            Message msg = Message.obtain();
            msg.what = what;
            return sendMessageDelayed(msg, delayMillis);
        }

     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);
        }

    辗转反则最后调用了sendMessageAtTime,在此方法内部有直接获取MessageQueue然后调用了enqueueMessage方法。我们再来看看此方法:

     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }

    enqueueMessage中首先为meg.target赋值为this,【假设大家还记得Looper的loop方法会取出每一个msg然后交给msg,target.dispatchMessage(msg)去处理消息】,也就是把当前的handler作为msg的target属性。终于会调用queue的enqueueMessage的方法,也就是说handler发出的消息,终于会保存到消息队列中去。


    如今已经非常清楚了Looper会调用prepare()和loop()方法。在当前运行的线程中保存一个Looper实例,这个实例会保存一个MessageQueue对象,然后当前线程进入一个无限循环中去。不断从MessageQueue中读取Handler发来的消息。然后再回调创建这个消息的handler中的dispathMessage方法,以下我们赶快去看一看这种方法:

    public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }

    能够看到。第10行。调用了handleMessage方法,以下我们去看这种方法:

      /**
         * Subclasses must implement this to receive messages.
         */
        public void handleMessage(Message msg) {
        }
        
    能够看到这是一个空方法,为什么呢,由于消息的终于回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后依据msg.what进行消息处理。

    比如:

    private Handler mHandler = new Handler()
    	{
    		public void handleMessage(android.os.Message msg)
    		{
    			switch (msg.what)
    			{
    			case value:
    				
    				break;
    
    			default:
    				break;
    			}
    		};
    	};

    到此。这个流程已经解释完毕,让我们首先总结一下

    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)终于调用的方法。

    好了。总结完毕,大家可能还会问。那么在Activity中。我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler能够成功创建呢。这是由于在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。

    3、Handler post

    今天有人问我,你说Handler的post方法创建的线程和UI线程有什么关系?

    事实上这个问题也是出现这篇博客的原因之中的一个;这里须要说明。有时候为了方便,我们会直接写例如以下代码:

    mHandler.post(new Runnable()
    		{
    			@Override
    			public void run()
    			{
    				Log.e("TAG", Thread.currentThread().getName());
    				mTxt.setText("yoxi");
    			}
    		});

    然后run方法中能够写更新UI的代码,事实上这个Runnable并没有创建什么线程,而是发送了一条消息,以下看源代码:

     public final boolean post(Runnable r)
        {
           return  sendMessageDelayed(getPostMessage(r), 0);
        }
      private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r;
            return m;
        }

    能够看到,在getPostMessage中,得到了一个Message对象,然后将我们创建的Runable对象作为callback属性。赋值给了此message.

    注:产生一个Message对象。能够new  ,也能够使用Message.obtain()方法。两者都能够,可是更建议使用obtain方法,由于Message内部维护了一个Message池用于Message的复用。避免使用new 又一次分配内存。


     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);
        }
    终于和handler.sendMessage一样,调用了sendMessageAtTime。然后调用了enqueueMessage方法,给msg.target赋值为handler。终于增加MessagQueue.

    能够看到,这里msg的callback和target都有值,那么会运行哪个呢?

    事实上上面已经贴过代码,就是dispatchMessage方法:

     public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    第2行,假设不为null。则运行callback回调。也就是我们的Runnable对象。

    好了。关于Looper , Handler , Message 这三者关系上面已经叙述的非常清楚了。

    最后来张图解:


    希望图片能够更好的帮助大家的记忆~~

    4、后话

    事实上Handler不仅能够更新UI,你全然能够在一个子线程中去创建一个Handler。然后使用这个handler实例在不论什么其它线程中发送消息,终于处理消息的代码都会在你创建Handler实例的线程中运行。

    new Thread()
    		{
    			private Handler handler;
    			public void run()
    			{
    
    				Looper.prepare();
    				
    				handler = new Handler()
    				{
    					public void handleMessage(android.os.Message msg)
    					{
    						Log.e("TAG",Thread.currentThread().getName());
    					};
    				};
                                   Looper.loop();                                                                                                                              }             

    Android不仅给我们提供了异步消息处理机制让我们更好的完毕UI的更新。事实上也为我们提供了异步消息处理机制代码的參考~~不仅能够知道原理。最好还能够将此设计用到其它的非Android项目中去~~

    最新补充:

    关于后记,有兄弟联系我说,究竟能够在哪使用,见博客:Android Handler 异步消息处理机制的妙用 创建强大加载的类



    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    ZOJ 2158 Truck History
    Knight Moves (zoj 1091 poj2243)BFS
    poj 1270 Following Orders
    poj 2935 Basic Wall Maze (BFS)
    Holedox Moving (zoj 1361 poj 1324)bfs
    ZOJ 1083 Frame Stacking
    zoj 2193 Window Pains
    hdu1412{A} + {B}
    hdu2031进制转换
    openjudge最长单词
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4824195.html
Copyright © 2011-2022 走看看