Handler,Message,Looper & MessageQueue

##1 Handler简介
Handler,Looper,Message,MessageQueue是Android开发中经常遇到的知识点,也是Android面试题中常遇到的问题。

先来看一下Message在Android中传递的概览图

Message传递示意图

上面这张图基本反映了Handler发送和处理Message的流程。Handler调用sendMessage方法,发送给MessageQueue,Looper不断从MessageQueue中取出Message,并调用Handler的handleMessage方法。
我们就根据这张图,来分析一下整个发送,轮询,处理的流程。

#2 为什么要使用Handler

  1. 绝大多数GUI系统,都是使用事件驱动编程(event driven programming)的编程范式,Android也不例外。Handler就是消息的发送者和处理者。我们平时写Activity时常用的onCreate,onStart,onResume等函数都是在Handler中被回调的(可以参见ActivityThread.java文件的实现方式),因此学习Handler可以帮助我们更好的理解Android对事件的处理,理解Android组件的生命周期。

  2. Android被设计为不允许在子线程中更新UI(会抛出异常),同时也不允许在主线程做耗时的操作(容易引起ANR)。那么当应用需要做一个耗时的操作并将耗时操作(比如说HTTP请求)的结果更新到UI的时候怎么办呢?我们可以借助于Handler机制。在主线程中定义Handler对象,并重写handleMessage方法,同时开辟子线程,在子线程中执行完耗时操作以后,调用该Handler对象的sendMessage方法,那么Handler的handleMessage方法就会在主线程中执行。

#3 Handler的基本用法
##3.1 使用sendMessage方法发送Message
先来看一下Handler的基本用法。Handler的最常用的用法就是子线程执行耗时的操作,执行完以后向主线程更新UI。
下面试一个简单的代码示例.

public class MainActivity extends ActionBarActivity {
	private static final String TAG = "MainActiviy";
	private TextView mTextView;
	private Handler myHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			mTextView.setText("msg.what = "+ msg.what + "  msg.obj.value = " + ((Foo)msg.obj).value);
			Log.d(TAG, "msg.what = "+ msg.what + "  msg.obg->value = " + ((Foo)msg.obj).value);
		}
	};
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mTextView = (TextView)findViewById(R.id.text_view);
		
		new Thread(new Runnable(){
			public void run() {
				for (int i = 0 ; i < 5 ; i ++) {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					Message msg = myHandler.obtainMessage();
					msg.what = i;
					msg.obj = new Foo(2*i);
					myHandler.sendMessage(msg);
				}
			}
			
		}).start();
	}
}

class Foo {
	public int value;
	public Foo(int value) {
		this.value = value;
	}
}

在MainActivity中定义了一个匿名内部类,继承自Handler类,并重写其中的hanleMessage方法。将发送过来的Message对象的what 和 obj的值更新到TextView上。并定义此匿名内部类的对象 myHandler.
在onCreate方法中开辟子线程,每sleep一秒钟,通过Handler对象发送一个消息。消息携带的obj为一个Foo类的对象。

运行一下这个程序,控件被成功更新,并打印出如下Log:

D/MainActiviy(17679): msg.what = 0  msg.obg->value = 0
D/MainActiviy(17679): msg.what = 1  msg.obg->value = 2
D/MainActiviy(17679): msg.what = 2  msg.obg->value = 4
D/MainActiviy(17679): msg.what = 3  msg.obg->value = 6
D/MainActiviy(17679): msg.what = 4  msg.obg->value = 8

从Log可以看出子线程里发送的消息在主线程里被成功处理。至于原理,我们会在后面进行深入的讲解。

##3.2 使用post方法发送Message
不仅可以使用Handler的sendMessage(Message msg)方法来发送一个Message对象,还可以使用post(Runnable r)方法,发送一个Runnable对象。下面是一个使用post方法的简单demo。

public class MainActivity extends ActionBarActivity {
	private static final String TAG = "MainActivity";
	Handler mHandler = new Handler();
	Runnable mRunable = new Runnable() {
		public void run() {
			mTextView.setText(" recevie the message");
			Log.D(TAG, "handler demo2 Runnable run");
		}
	};
	TextView mTextView;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		mTextView = (TextView)findViewById(R.id.text_view);
		
		new Thread(new Runnable() {
			public void run() {
				try {
					Thread.sleep(3000);
					mHandler.post(mRunable);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}
}

打印出log:

D/MainActivity(26680): handler demo2 Runnable run

说明调用过post(Runnable r)方法以后,Runnable对象的run方法被调用执行。
post方法与sendMessage方法有几个不同的地方。

  1. 发送的是Runnable对象而不是Message对象
  2. 定义Handler对象的时候没有重写Handler的handleMessage方法。
  3. post方法的参数Runnable的run方法在主线程被调用,而不是handleMessage方法被调用。

上面已经用Demo的形式演示了如何使用Handler的 post 和 sendMessage方法。下一节将要学习为什么Handler可以post 一个Runnable对象,以及为什么在子线程中发送Message,handleMessage就会在主线程中被调用。

#4 Handler的原理
##4.1 消息传递的载体:Message
Message类是Handler发送消息的载体,也是MessageQueue的队列的成员。所以首先来了解一下Message类。完整的类可以参见framework的源码:Message.java。
我们只讲其中比较重要的几个属性:

public final class Message implements Parcelable {
    public int what;
	public Object obj;
	Handler target;
	Runnable callback;
	…
}

其中

  1. what是消息的标识符,说明消息的种类,类型。
  2. obj,obj是Message所携带的消息体。因为obj是一个Object类型的引用。在Java中,任何一个类都是派生于Object类。可以看出Message可以携带任意类型的消息。
  3. target:消息的接收者(即处理者),可以看到消息的接收者是一个Handler对象。因此可以知道在Android中,Message的接受者(处理者)也是Handler。
  4. callback,这个参数说明该Message自带处理回调,相当于自带处理方式的Message,他的类型是Runnable。

Message有很多属性,但是上述属性已经足够分析Handler原理了。

##4.2 消息的发送者和处理者:Handler
官方文档对于Handler用法的说明:
There are two main uses for a Handler:
(1) to schedule messages and runnables to beexecuted as some point in the future;
(2) to enqueue an action to be performed on a different thread than your own.

翻译成中文就是:Handler主要有两个作用:
1)是在将来的某个时间点执行Message和Runnable。
2)将动作(也就是Message和Runnable)放入消息队列,并在自己以外的另一个线程执行。
从官方的解释看出有些书本上所说的:Handler是用来从子线程向主线程更新UI,这个说法是不准确的,至少没有完全描述出Handler的作用。
###4.2.1 Handler的创建
我们调用Handler的sendMessage方法和post方法来发送消息,而在4.1节中看到Message的target属性也是Handler,所以Message的发送者和处理者都是Handler。
Handler的构造方法:

public Handler(Callback callback, boolean async) {
	…
    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;
}

在构造Handler对象的时候,首先指定调用Looper.myLooper()方法,Looper我们后面会再介绍。这里大家先了解这个是为了获取当前线程的Looper对象。把当前线程的Looper对象赋给了mLooper,并且通过mQueue = mLooper.mQueue获取当前线程的Looper所对应的MessageQueue。从这一段代码可以看出当创建Handler对象的时候,Handler对象就会和当前线程的Looper对象以及Looper对象中的MessageQueue绑定在一起。

而如果当前线程没有Looper对象,则会抛出RuntimeException,说明不允许在没有Looper对象的线程中创建Handler对象。Activity和Service所在主线程中,系统已经创建打了一个MainLooper,所以可以直接在主线程中创建Handler对象,而该Handler对象则绑定到主线程的Looper对象(即MainLooper),以及主线程Looper对象对应的MessageQueue。

###4.2.2 Handler消息的发送: sendMessage
Handler归属于sendMessage方法的函数簇的签名如下:

public final boolean sendEmptyMessage(int what)
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) 
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
public final boolean sendMessage(Message msg)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
public boolean sendMessageAtTime(Message msg, long uptimeMillis)

主要分为sendEmptyMessage和SendMessage两大类,每一类又分为直接发送,延时发送,和在指定时间发送。
首先来看sendEmptyMessage这一大类:

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

当调用sendEmptyMessage的时候,Handler会为构建一个空的消息体,并调用sendMeesage类方法。因此sendEmptyMessage是sendMessage方法的一个简写的形式。所以只需集中关注一下sendMessage这一类的方法。
sendMessage,sendMessageDelayed,最终都是调用到sendMessageAtTime,因此直接分析一下sendMessageAtTime方法。

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

方法体很简单,将参数中的Message对象,调用enqueueMessage(…)方法,Message放入MessageQueue。而所要放入的目标MessageQueue正是一开始在创建Handler的时候所绑定的对应线程的Looper对象对应的MessageQueue。
从这里可以看到调用sendMessage方法是如何将Message消息体发送到Handler所关联的MessageQueue中的。
再来看一下enqueueMessage方法

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

注意到这样一行msg.target=this,在4.1中讲过target表示Message对象的处理者,很显然,Handler在将Message放入消息队列的时候,指定了Message将来的处理者就是我自己。
###4.2.3 Handler消息的发送: post
之前讲过Handler除了可以调用SendMessage方法发送Message对象,还可以调用post方法来发送Runnable对象。归属于post的函数簇的签名如下:

public final boolean post(Runnable r)
public final boolean postAtTime(Runnable r, long uptimeMillis)
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
public final boolean postDelayed(Runnable r, long delayMillis)

举一个典型的例子:

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

采用post发送也是sendMessage方法的一种表现形式。首先构建了一个空的Message对象,将Message对象的callback置为传入的Runnable对象。在4.1中介绍Message介绍到Message的callback属性,这个是消息自带的处理函数。说明post一个Runnable对象的时候,也是发送了一个Message对象,只不过这个Message消息体是自带处理callback处理函数的。

###4.2.4 Handler的消息处理 dispatchMessage(Message msg)
Handler的处理函数为dispatchMessage(Message msg)

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

Handler在处理收到的Message的时候,首先会查看Message是否存在callback,若存在则调用Message对象的callback处理消息。即我们在使用post方法发送消息时传入的Runnable对象。
若callback为null,则会走到handleMessage方法,即通常定义Handler类的时候重写的方法。
###4.2.5 Handler类小结
4.2节分析了Handler的构造函数,发送Message对象的方式(sendMessage,post)和处理Message的方式(dispatchMessage)。

在构造Handler对象的时候,Handler会绑定到创建Handler时候所在的线程的Looper和MessageQueue。

Handler有两种方式发送消息,post(Runnable r)和sendMessage(Message m)。post方法的核心思想是,构造一个Message对象,该Message的callback为定义的 Runnable,再将此Message对象通过sendMessage方法发送到消息队列中去。当调用Handler的sendMessage的方法的时候,Handler会将Message放入创建时绑定的MessageQueue当中,并且指定Message的target(即消息的接收者)为自己。这样保证将来这个消息仍然由自己处理。

当Handler处理消息的时候。会查询该消息是否带有callback,若消息带有callback,这样的消息通常由post(Runnable r)方法发送,则调用Runnable中定义的run()方法来处理函数。若Message若没有携带callback,则使用handleMessage来处理Message。
##4.3 MessageQueue的持有者:Looper
前面已经讲了Message本身,Message的发送,Message的处理。那么保存Message的MessageQueue又是存在于哪里的呢?而又是谁不停的从MessageQueue中读取Message,再将其处理的呢?答案就是Looper。
###4.3.1 创建Looper
Looper的构造函数是私有的,我们无法直接构建Looper对象。Looper为我们暴露的接口是prepare();

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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类为我们维护了一个类型为ThreadLocal的sThreadLocal变量,如果大家想深入了解,可以百度一下ThreadLocal的用法。继续阅读本文则只需要了解,Looper为每一个调用prepare()的线程创建了一个Looper对象,并且关联到当前线程。所以说一个线程默认是没有Looper对象的,而且如果在一个已经调用过Looper.parepare()方法的线程中再次调用prepare(),则会抛出异常。由此可以看出:一个线程最多只能持有一个Looper对象。
再来看一下Looper的构造函数:

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

构建Looper对象的时候的动作很简单,创建一个消息队列,并且记录当前线程。说明MessageQueue是创建在Looper当中。由之前线程和Looper对象的关系可知。一个Thread最多持有一个Looper,该Looper包含一个MessageQueue。而线程默认是没有Looper的,也不存在消息队列。

###4.3.2 轮询消息并处理

public static void loop() {
  	…
  	for (;;) {
	    Message msg = queue.next(); // might block
      	….
        msg.target.dispatchMessage(msg);
		….
    }
}

线程创建Looper以后,调用Looper.loop()则进入一个死循环,不停的轮询消息队列,取出Message,并调用Message的接收者(即target)的处理函数(即dispatchMessage)。

#5 总结
我们从介绍了Message这个类的属性,Handler的创建,并学习了使用Handler发送和处理Message的接口,重点是Handler在创建的时候是如何绑定到线程的Looper和MessageQueue对象的,以及是如何将Message对象放入指定的MessageQue的。最后学习了Looper对象是如何从MessageQueue中获取Message,并且调用Message的target的处理函数进行处理。

我们以回答面试的时候关于Handler的几个问题来结束本文:

1. 为什么可以发送Runnable对象?
使用post(Runnable r)方法,首先构造一个空的Message,再将Message的callback置为Runnable对象r,再调用sendMessage方法发送该Message。post方法是sendMessage方法的一个变体。而当该消息被处理的时候,因为该Message自带callback,所以callback(也即Runnable对象 r)将会被调用。
2. Message为什么会在不同的线程里进行处理,Handler是如何识别当前线程,又是如何在另外一个线程里运行的?
Handler在创建的时候会记录当前的线程,当前线程的Looper,当前线程Looper对应的MessageQue。因此Handler是绑定到一个线程的。而此后Handler发送的消息都会发送到Handler所绑定的MessageQueue当中,并且指定Message的target也就是处理者为this。该线程的Looper对象轮询到Message以后又会在该线程中调用Message.target.dispatchMessage。因此Handler可以在任意线程中发送消息,但是发送的消息都是在创建他的线程中进行处理的。
3. Looper,MessageQue,线程以及Handler他们之间是怎么样的对应关系?
线程默认是没有Looper和MessageQue的。调用Looper.prepare()以后,则为当前线程创建了一个Looper,一个Looper含有一个MessageQueue。所以一个线程最多持有一个Looper和一个MessageQueue。
Handler在创建的时候会绑定到当前线程的Looper,MessageQueue
4. Looper取到Message以后又是如何找到发送他的Handler,再调用对应的handleMessage方法的呢?
Looper在调用loop()方法进入消息轮询以后,从消息队列中取出Message,调用Message.target.dispatchMessage,由于在使用Handler发送消息的时候指定了消息的target是this引用,所以该消息最终将由发送该消息的Handler的dispatchMessage方法进行处理。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值