全面解析Android之Handler机制

      这篇文章将带着大家全方位去理解Handler机制,我们先将涉及几个重要的类列出来:

Handler: 发送消息到MessageQueue;Looper循环处理消息后,交给Handler的handleMessage(..)进行处理。

MessageQueue:  顾名思义,消息队列,用于存储消息数据。以队列先进先出的特点,对外提供消息的插入和删除操作;但是,MessageQueue数据结构实际是单链表。

Looper:  顾名思义,循环消息。当MessageQueue中有消息时,Looper循环不断的处理消息;当MessageQueue中消息为空时,Looper阻塞在原地。

ThreadLocal:  不是线程,是一个可以存储数据的类。ThreadLocal在取值操作的时候会传入一个线程引用,使ThreadLocal在不同线程中取出的变量引用的值不同。

Message:  顾名思义,消息。用于封装消息的数据,字段what表示消息唯一标示,obj,arg1,arg2表示存储在消息中的数据。

ActivityThread:  主线程,又UI线程。其在创建的时候,就会初始化线程中的Looper。


接下里,先说明下Handler机制为什么要存在?它能解决什么问题?

Android做一个耗时操作的时候,需要在子线程中进行处理,否则会出现ANR;在执行完耗时操作后,需要将数据用于UI的刷新,操作UI是要在UI线程中完成,否则系统会报错。
而,Handler机制就可以实现线程间的通信,将数据封装到消息中,并将消息发送给UI线程,取出消息中数据刷新UI。

后面我们要分析的是:消息是如何通过MessageQueue,Looper,最终实现消息从子线程传递到UI线程的?


第一步,将子线程中获取的数据封装在Message中,第一步是要发送Message到MessageQueue,常调用的方法有:handler.sendMessage(..),message.sendToTarget()。

sendMessage的源码如下:

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
实际上最终调用的是这样一个方法,如下:

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

进入方法enqueueMessage(..),如下:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
调用的是MessageQueue的enqueueMessage(..),将消息插入到消息队列中,继续查看MessageQueue源码如下:

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

其实在handler.sendMessage(..)方法里,是有如下这样的一段注释的,大致意思是:将消息放到消息队列的末尾,并在handler所在线程被接受,handleMessage进行处理。

/**
     * Pushes a message onto the end of the message queue after all pending messages
     * before the current time. It will be received in {@link #handleMessage},
     * in the thread attached to this handler.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */

message.sendToTarget()里面实际调用就是handler.sendMessage(..),感兴趣的同学可以查阅源码去验证,这里不作阐述。

第二步:通过前面我们知道在ActivityThread创建的时候,就已经初始化Looper了。其实,在创建Handler的实例时,如果handler所在线程没有初始化Looper,那么会报一个异常。这里小王君做过一个测试,在子线程里创建Handler实例,没有初始化Looper,报了一个如下异常:

05-28 15:13:50.300: E/AndroidRuntime(2932): FATAL EXCEPTION: Thread-511
05-28 15:13:50.300: E/AndroidRuntime(2932): Process: com.example.test, PID: 2932
05-28 15:13:50.300: E/AndroidRuntime(2932): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
05-28 15:13:50.300: E/AndroidRuntime(2932): 	at android.os.Handler.<init>(Handler.java:208)
05-28 15:13:50.300: E/AndroidRuntime(2932): 	at android.os.Handler.<init>(Handler.java:122)
05-28 15:13:50.300: E/AndroidRuntime(2932): 	at com.example.test.MainActivity$1$1.<init>(MainActivity.java:25)
05-28 15:13:50.300: E/AndroidRuntime(2932): 	at com.example.test.MainActivity$1.run(MainActivity.java:25)
05-28 15:13:50.602: E/(2932): appName=com.example.test, acAppName=/system/bin/surfaceflinger
05-28 15:13:50.602: E/(2932): 0
Can't create handler inside thread that has not called Looper.prepare(),意思很明显:无法在一个没有调用Looper.prepare()所在的线程里创建handler。我们可以查阅Handler构造函数验证报错原因,留给感兴趣的同学研究,这里不作阐述。

这就间接验证了:为啥我们在主线程里随意创建handler对象,并没有报错的原因,因为主线程在创建的时候就初始化了Looper。那么在子线程中该如何创建Handler呢?查阅Looper源码有这样一段注释,很好告知处理方式,注释如下:

/**
  * Class used to run a message loop for a thread.  Threads by default do
  * not have a message loop associated with them; to create one, call
  * {@link #prepare} in the thread that is to run the loop, and then
  * {@link #loop} to have it process messages until the loop is stopped.
  * 
  * <p>Most interaction with a message loop is through the
  * {@link Handler} class.
  * 
  * <p>This is a typical example of the implementation of a Looper thread,
  * using the separation of {@link #prepare} and {@link #loop} to create an
  * initial Handler to communicate with the Looper.
  *
  * <pre>
  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *
  *      public void run() {
  *          Looper.prepare();
  *
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *
  *          Looper.loop();
  *      }
  *  }</pre>
  */

接下来, 介绍下Looper的两个很关键的方法:Looper.prepare(),Looper.loop();

Looper.prepare(),用于初始化Looper,源码如下:

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));
    }
分析:这里初始化Looper不是简单的创建对象,还需要存储Looper对象,操作的类是ThreadLocal。前面有ThreadLocal类的解释,目的就是为了handler可以存入和取出所在线程的Looper。很显然每个线程的Looper是不同的,我们也可以用Hash表结构的类存取Looper,但是系统没有这么做,而是提供一个ThreadLocal解决需求。接下来进入ThreadLocal的源码查看方法,存入set(...),取出get()。ThreadLocal的set(..)源码,如下:

public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }

Values values(Thread current) {
        return current.localValues;
    }
 Values initializeValues(Thread current) {
        return current.localValues = new Values();
    }
void put(ThreadLocal<?> key, Object value) {
            cleanUp();

            // Keep track of first tombstone. That's where we want to go back
            // and add an entry if necessary.
            int firstTombstone = -1;

            for (int index = key.hash & mask;; index = next(index)) {
                Object k = table[index];

                if (k == key.reference) {
                    // Replace existing entry.
                    table[index + 1] = value;
                    return;
                }

                if (k == null) {
                    if (firstTombstone == -1) {
                        // Fill in null slot.
                        table[index] = key.reference;
                        table[index + 1] = value;
                        size++;
                        return;
                    }

                    // Go back and replace first tombstone.
                    table[firstTombstone] = key.reference;
                    table[firstTombstone + 1] = value;
                    tombstones--;
                    size++;
                    return;
                }

                // Remember first tombstone.
                if (firstTombstone == -1 && k == TOMBSTONE) {
                    firstTombstone = index;
                }
            }
        }
分析:如果values为空,执行代码current.localValues = new Values(),values = current.localValues,current即Thread。put(...)方法在ThreadLocal的静态内部类-Values类里面,核心代码是调用values.put(ThreadLocal,Looper),变量values对象持有当前Thread的引用(values = current.localValues)。

put方法就是将Looper对象放入一个数组里(代码里变量value就是传入的Looper对象),由于每个线程都对应一个新的Values对象,因此存入Looper是在不同的数组中操作的。

于是,ThreadLocal类就可以正确的存入线程所在Looper的对象,而不会乱存其他线程的Looper。

总结:Looper.prepare()里面不仅创建了Looper对象,还使用ThreadLocal类实现了Looper于所在线程中的存入。


继续分析Looper.loop(),大家主要看下在源码中的一些注释,源码如下:

/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
	//获取Looper对象
        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进行转发处理
            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();
        }
    }

分析:

1,final Looper me = myLooper(),源码如下:

/**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }
于是查看 ThreadLocal的get方法源码,如下:

public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }
该方法取出所在线程的Looper对象。先取出Values对象里的数组table(Object[] table = values.table),执行一个&运算得到角标index,存入的数据在角标index + 1位置,于是looper = table[index + 1]。

2,final MessageQueue queue = me.mQueue;消息队列mQueue的实例,在创建Looper对象时创建,也就是我们调用Looper.prepare()时创建。

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mRun = true;
        mThread = Thread.currentThread();
    }
3, Message msg = queue.next();再次进入到MessageQueue类中查看next方法,源码如下:

Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;

        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            nativePollOnce(mPtr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuiting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }
取出消息队列中消息return,并在队列中移除该消息,核心代码如下:

	// Got a message.
            mBlocked = false;
            if (prevMsg != null) {
                    prevMsg.next = msg.next;
             } else {
                     mMessages = msg.next;
               }
             msg.next = null;
             if (false) Log.v("MessageQueue", "Returning message: " + msg);
             msg.markInUse();
             return msg;
4,取出消息之后,调用方法 msg.target.dispatchMessage(msg)进行转发处理,target就是Handler对象,进入Handler源码查看,如下:

/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
由代码可知,处理消息的方式有三种:

4.1, msg.callback != null时,调用方法handleCallback(msg),该方法里就一行代码:message.callback.run();callback就是一个Message里的Runnable接口,这里是回调接口的run方法。 

在调用handler.post(runnable)方法发送消息时,callback不为空,处理消息的代码放在run方法里。handler.post(runnable)源码,如下:

/**
     * Causes the Runnable r to be added to the message queue.
     * The runnable will be run on the thread to which this handler is 
     * attached. 
     *  
     * @param r The Runnable that will be executed.
     * 
     * @return Returns true if the Runnable was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

注意:方法上有这样一条注释,Causes the Runnable r to be added to the message queue.The runnable will be run on the thread to which this handler is attached。说的意思就是runnable 的执行是在handler所在线程里,我们这里就是UI线程。

getPostMessage(r)就是创建一个callback变量的值为r的消息,查看其源码,如下:

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
sendMessageDelayed就是前面讲到发送消息到消息队列时调用的方法,这里不作阐述。

4.2,mCallback != null时,mCallback 是接口Handler$Callback的引用,执行mCallback.handleMessage(msg)处理消息。Handler$Callback源码,如下:

/**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     */
    public interface Callback {
        public boolean handleMessage(Message msg);
    }
注释大致意思是:当去实例化一个Handler对象时,如果不想派生其子类,可以用Handler h = new Handler(mCallback)创建对象。

4.3:当以上两个接口都没有实例化时,调用handleMessage(msg)处理消息,源码如下:

/**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }
我们发现这个方法里面是空的,什么也不做,查看注释发现Android要求在创建Handler子类时,必须重写该方法接受消息并处理。当然,这是开发中最常用的处理消息方式。


总结下handler机制,大体是这样的:

1,首先将子线程中获取数据封装在Message对象中,;

2,通过handler的sendMessage,或post方法将消息发送到消息队列里,最终都走得是sendMessageAtTime这条路,sendMessageAtTime里面调用了MessageQueue的enqueueMessage方法,完成消息插入到消息队列的操作;

3,每当我们创建Handler实例时,就会初始化Looper对象,是调用Looper.prepare()创建Looper对象(同时MessageQueue对象创建在Looper构造函数中完成),并将Looper对象通过ThreadLocal类存入到线程中;

4,Looper从消息队列里不断取出消息,是调用Looper.loop(),里面调用了MessageQueue的next方法,将消息取出来;

5,取出来的消息通过handle的dispatchMessage(..)转发处理,具体处理方式有三种,如上所示。


:学习到这里,可能都有一个这样的疑问:哇~程序执行是如何从子线程慢慢到主线程的?

对,就是调用Handler所在主线程的Looper.loop()方法,抽取到消息处理的时候,代码就执行到主线程了,是不是很神奇,这也说明Looper对于线程间通信简直太重要了!


下面将粘贴王小君实际开发中,利用handler机制实现线程通信的三个不同example,如下:

4.1:

private Handler h = new Handler();
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		new Thread(){
			public void run() {
				SystemClock.sleep(6000);
				h.post(new Runnable() {
					@Override
					public void run() {
						Toast.makeText(getApplicationContext(), "run方法是在主线程里执行的", 0).show();
					}
				});
			};
		}.start();
	}:
4.2,:

private static final int SHOW_UI = 0;
	private Handler h = new Handler(new Handler.Callback(){
		@Override
		public boolean handleMessage(Message msg) {
			switch (msg.what) {
			case SHOW_UI:
				Toast.makeText(getApplicationContext(), "不想派生handler的子类处理消息", 0).show();
				break;

			default:
				break;
			}
			return false;
		}
	});
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		new Thread(){
			public void run() {
				SystemClock.sleep(6000);
				Message msg = Message.obtain();
				msg.what = SHOW_UI;
				h.sendMessage(msg);
			};
		}.start();
	}

4.3:

private static final int SHOW_UI = 0;
	Handler h1 = new Handler(){
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case SHOW_UI:
				Toast.makeText(getApplicationContext(), "最常使用处理消息的方式", 0).show();
				break;

			default:
				break;
			}
	};
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		new Thread(){
			public void run() {
				SystemClock.sleep(6000);
				Message msg = Message.obtain();
				msg.what = SHOW_UI;
				h.sendMessage(msg);
			};
		}.start();
	}


阅读到这里,相信大家对Android的Handler机制有一个全面的理解了。文章开头就列出涉及的一些类,供大家查阅理解;然后分两步:发送消息,处理消息,从源码角度细致分析了实现原理;最后列出三个例子,供大家在实际开发中参考使用。

最后,辛苦大家能看到这里,文章有不够准确的地方,期待大家能在下面评论中指出,一起学习进步!!!

———小王君       (*^__^*) 








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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值