【Handler】Looper 原理 详解 示例 总结

核心知识点

1、相关名词
  • UI线程:就是我们的主线程,系统在创建UI线程的时候会初始化一个Looper对象,同时也会创建一个与其关联的MessageQueue
  • Handler:作用就是发送与处理信息,如果希望Handler正常工作,在当前线程中必须要有一个Looper对象
  • Message:Handler接收与处理的对象。Handler也能接收与处理Runnable对象
  • MessageQueue:消息队列,先进先出管理Message,在初始化Looper对象时会创建一个与之关联的MessageQueue
  • Looper:每个线程只能够有一个Looper,Looper负责创建并管理当前线程中的MessageQueue,调用loop方法后就会在一个无限循环体中不断地从MessageQueue中取出Message并分发给对应的Handler,最后回调handleMessage()方法处理此消息。Looper才是整个机制的核心!

2、基本使用过程:
  • 在主线程中创建Handler并重写handleMessage()方法
  • 在任何线程中都可以利用此Handler发送消息,消息会被发送到主线程的MessageQueue中
  • 一旦MessageQueue中有新消息,主线程中的 Looper 就会发现此消息,然后就会调用Handler的handleMessage()方法处理此消息

3、一些细节:
  • 调用Looper.prepare()后首先会在本线程中保存唯一的一个Looper实例,然后会在该实例中创建并保存一个MessageQueue对象;Looper.prepare()在一个线程中只能调用一次,MessageQueue在一个线程中也只存在一个
  • 调用Looper.loop()方法后会让当前线程进入一个无限循环中,Looper不断从MessageQueue中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
  • Handler的构造方法中,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue相关联
  • Handler的sendMessage方法,会给msg.target赋值为handler自身,然后加入MessageQueue中。
  • 在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。

Handler面试点

1、Handler的产生背景
Handler是线程间通讯的机制,Android中,网络访问、文件处理等耗时操作必须放到子线程中去执行,否则将会造成ANR异常。
ANR异常:Application Not Response 应用程序无响应
产生ANR异常的原因:在主线程执行了耗时操作,对Activity来说,主线程阻塞5秒将造成ANR异常,对BroadcastReceiver来说,主线程阻塞10秒将会造成ANR异常。
解决ANR异常的方法:耗时操作都在子线程中去执行
但是,Android不允许在子线程去修改UI,可我们又有在子线程去修改UI的需求,因此需要借助Handler(但是,一定要明白,Handler的作用不是为了在子线程去修改UI,而是为了实现线程间通讯!一定要明白理念与表现的区别)。

2、Handler机制相关概念
  • Looper:一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列)。
  • Handler:你可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里;或者接收Looper从Message Queue取出发送来的消息。
  • Message Queue(消息队列):用来存放线程放入的消息。
  • 线程:UIthread 通常就是main thread,而Android启动程序时会替它建立一个MessageQueue。

3、基本过程:
UI线程创建时,就创建了一个Looper,Looper内部维护这一个MessageQueue。
Looper通过开启一个while(true)死循环来轮询MessageQueue中的Message。
当Looper轮询到Message时,就分发此Message。

4、Looper和Handler之间的关联
Handler在子线程发送消息到MessageQueue,Message被Looper取出来后,分发给handler的handleMessage方法来处理。
那么,为什么说一个Looper可以对应多个Handler,Looper如何保证哪个Handler发出去的Message将交由哪个handler来处理?
因为Handler在发送Message的时候,在Message的成员变量target上标记了当前handler的引用:
message.target = this;//this即这个发送此Message的handler对象
当Looper取到message时,通过下面的方法分发Message:
message.target.dispatchMessage(message);  //message.target即发送此Message的handler对象
因此,哪个handler发送的Message,将由哪个Handler来处理此Message。


Looper详解

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

ThreadLocal可以在一个线程中存储变量,可以看到,我们将一个Looper的实例放入了ThreadLocal,并且判断了sThreadLocal是否存储过Looper对象,存储过(即.get()不为null)则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例

下面看其构造方法:
private Looper(boolean quitAllowed) {
	mQueue = new MessageQueue(quitAllowed);
	mRun = true;
	mThread = Thread.currentThread();
}
在构造方法中,创建了一个消息队列MessageQueue。

loop()方法

public static void loop() {
	final Looper me = myLooper();//方法体为 return sThreadLocal.get();  即此方法直接返回了sThreadLocal存储的Looper实例
	if (me == null) throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");//如果me为null则抛出异常,也就是说looper方法必须在prepar()之后运行。
	final MessageQueue queue = me.mQueue;//拿到该looper实例中的消息队列
	Binder.clearCallingIdentity(); // Make sure the identity of this thread is that of the local process, and keep track of what that identity token actually is.  
	final long ident = Binder.clearCallingIdentity();
	//无限循环
	for (;;) {
		Message msg = queue.next(); // might block,取出一条消息,如果没有消息则阻塞等待
		if (msg == null) return;
		Printer logging = me.mLogging; // This must be in a local variable, in case a UI event sets the logger  
		if (logging != null) logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what);
		msg.target.dispatchMessage(msg);//Msg的target其实就是handler对象
		if (logging != null) logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
		final long newIdent = Binder.clearCallingIdentity();// Make sure that during the course of dispatching the identity of the thread wasn't corrupted.  
		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主要作用:
  • 1、与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
  • 2、执行loop()方法,不断从MessageQueue中取消息,交给消息的target属性的dispatchMessage方法去处理。

大家可能还会问,在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢?
这是因为在Activity的启动代码中,已经默默的在当前UI线程中调用了Looper.prepare()和Looper.loop()方法。
而如果我们要想在子线程中创建Handler,则必须先调用Looper.prepare()方法,Handler创建后再调用Looper.loop()方法。

Handler详解

使用Handler之前,我们都是初始化一个实例,我们可以在声明的时候直接初始化,或者在onCreate中初始化。
我们首先看Handler的构造方法,看其如何与MessageQueue联系上的,它在子线程中发送的消息(当然可以在任何线程)是怎么发送到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();//获取当前线程保存的Looper实例
	if (mLooper == null) throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");//在初始化Handler之前必须先通过Looper.prepare()方法创建Looper的实例
	mQueue = mLooper.mQueue;//获取这个Looper实例中保存的MessageQueue(消息队列)
	mCallback = callback;
	mAsynchronous = async;
}
然后看我们最常用的发送消息相关的几个方法
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,最后调用queue的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去。【上面我们说了,Looper的loop方法会取出每个msg然后交给msg.arget.dispatchMessage(msg)去处理消息】,也就是说Looper的loop方法会把取出的每个msg通过当前的handler的dispatchMessage回调处理

下面我们去看一看handler中的这个dispathMessage方法
public void dispatchMessage(Message msg) {
	if (msg.callback != null) handleCallback(msg);
	else {
		if (mCallback != null) {
			if (mCallback.handleMessage(msg)) return;
		}
		handleMessage(msg);
	}
}

可以看到,最后调用了handleMessage方法,可实际上这是一个空方法,为什么呢?因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理。


Handler的全部方法

构造方法

其他方法

获取消息

移除消息和回调

发送消息

post方法

有时候为了方便,我们会直接写如下代码:

mHandler.post(new Runnable() {
	@Override
	public void run() {
		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();//得到了一个Message对象
	m.callback = r;//将我们创建的Runable对象作为callback属性,赋值给了此Message
	return m;
}  
后面的步骤就和handler.sendMessage一样了
可以看到,这里msg的callback和target都有值,那么会执行哪个呢?
其实上面已经贴过代码,就是dispatchMessage方法:如果msg.callback不为null,则执行callback回调,也就是我们定义的Runnable。

演示代码

public class HandlerTestActivity extends ListActivity {
	private TextView tv_info;
	private Handler uiHandler;
	private StaticThread thread;//一个子线程
	
	public static final int MSG_WHAT_1 = 1;
	public static final int MSG_WHAT_2 = 2;
	public static final int MSG_WHAT_3 = 3;
	
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		String[] array = {"开启子线程,并在子线程中创建一个Handler", //
				"在主线程中,通过子线程的Handler[向子线程]发消息", //
				"演示Handler的post方法"};
		tv_info = new TextView(this);
		tv_info.setText("Handler、Looper、Message、MQ、Thread关系");
		getListView().addFooterView(tv_info);
		setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new ArrayList<String>(Arrays.asList(array))));
		
		uiHandler = new StaticUiHandler(this); //系统启动时已经为主线程初始化了Looper、MQ等,我们可以直接创建Handler
		thread = new StaticThread(this);
	}
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		if (uiHandler != null) uiHandler.removeCallbacksAndMessages(null);
		if (thread != null && thread.getAnsyHandler() != null) thread.getAnsyHandler().removeCallbacksAndMessages(null);
	}
	
	@Override
	protected void onListItemClick(ListView l, View v, int position, long id) {
		switch (position) {
			case 0://开启子线程,并在子线程中创建一个Handler
				if (thread != null && !thread.isAlive()) thread.start();//A thread is alive if it has been started and has not yet died.
				break;
			case 1://在主线程中,通过子线程的Handler[向子线程]发消息
				Message msg = Message.obtain(null, MSG_WHAT_1, "消息内容"); //第一个参数Handler的作用是指定msg.target
				//这里设为null的原因是:后面调用sendMessage方法时重新指定了发送此消息的Handler为msg.target
				if (thread != null && thread.getAnsyHandler() != null) thread.getAnsyHandler().sendMessage(msg);
				tv_info.append("
1、在UI线程中用子线程的Handler发消息,what=" + msg.what);
				break;
			case 2:
				//其实这个Runnable并没有创建什么线程,而是发送了一条消息,当Handler收到此消息后回调run()方法
				uiHandler.post(() -> tv_info.append("
演示Handler的post方法"));
				break;
		}
	}
	//***********************************************静态内部类,防止内存泄漏*******************************************
	
	/**
	 * 主线程使用的Handler
	 */
	private static class StaticUiHandler extends Handler {
		private SoftReference<HandlerTestActivity> mSoftReference;
		
		public StaticUiHandler(HandlerTestActivity activity) {
			mSoftReference = new SoftReference<>(activity);
		}
		
		@Override
		public void handleMessage(Message msg) {
			HandlerTestActivity activity = mSoftReference.get();
			if (activity != null && activity.thread != null && activity.thread.getAnsyHandler() != null) {
				activity.tv_info.append("
4、UI线程的Handler收到消息,what=" + msg.what);
				Message msg3 = Message.obtain(null, MSG_WHAT_3, msg.obj);
				activity.thread.getAnsyHandler().sendMessageAtTime(msg3, SystemClock.uptimeMillis() + 2000);
				activity.tv_info.append("
5、在UI线程中用子线程的Handler发消息,what=" + msg3.what);
			}
		}
	}
	
	/**
	 * 异步线程(子线程)使用的Handler
	 */
	private static class StaticAnsyHandler extends Handler {
		private SoftReference<HandlerTestActivity> mSoftReference;
		
		public StaticAnsyHandler(HandlerTestActivity activity) {
			mSoftReference = new SoftReference<>(activity);
		}
		
		@Override
		public void handleMessage(Message msg) {
			HandlerTestActivity activity = mSoftReference.get();
			
			if (activity != null) {
				final Message tempMsg = Message.obtain(msg);//把收到的消息保存起来
				//注意,一定要注意!根据消息池机制,当此消息不再在【此子线程】中使用时,此msg会立即被重置(引用虽在,内容为空)
				//所以,如果想把此消息转发到其他线程,或者想在其他线程中引用此消息,一定要手动把消息保存起来!
				
				activity.runOnUiThread(() -> {//在子线程中创建Handler的目的是为了和其他线程通讯,绝对不是(也不能)更新UI
					activity.tv_info.append("
2、子线程的Handler收到消息,what=" + tempMsg.what);
					
					if (activity.uiHandler != null && tempMsg.what == MSG_WHAT_1) {
						Message msg2 = Message.obtain(null, MSG_WHAT_2, tempMsg.obj);
						activity.uiHandler.sendMessageDelayed(msg2, 2000);
						//注意,不能直接把一条还在使用的消息转发出去,否则IllegalStateException: This message is already in use
						activity.tv_info.append("
3、在子线程中用UI线程的Handler发消息,what=" + msg2.what);
					}
				});
			}
		}
	}
	
	/**
	 * 一个线程,用于执行耗时的操作
	 */
	private static class StaticThread extends Thread {
		private SoftReference<HandlerTestActivity> mSoftReference;
		
		public StaticThread(HandlerTestActivity activity) {
			mSoftReference = new SoftReference<>(activity);
		}
		
		private Handler ansyHandler;
		
		public Handler getAnsyHandler() {
			return ansyHandler;
		}
		
		public void run() {
			HandlerTestActivity activity = mSoftReference.get();
			if (activity != null) {
				Looper.prepare(); //在创建Handler【前】必须调用此方法初始化Looper,否则直接报否则报RuntimeException崩溃
				//里面做的事情:①为当前线程创建唯一的Looper对象  ②在它的构造方法中会创建一个的MessageQueue对象
				//此方法只能被调用一次,这保证了在一个线程中只有一个Looper实例以及只有一个与其关联的MessageQueue实例
				ansyHandler = new StaticAnsyHandler(activity);  //任何线程都可通过此Handler发送信息!
				Looper.loop(); //若要能够接收到消息,创建Handler后,必须调用loop方法。当然此方法必须是在prepar之后执行
				//里面做的事情:启动一个死循环,不断从MQ中取消息,没有则阻塞等待,有则将消息传给指定的Handler去处理
				activity.runOnUiThread(() -> Toast.makeText(activity, "会一直阻塞在这里", Toast.LENGTH_SHORT).show());
			}
		}
	}
}
2017-8-25
原文地址:https://www.cnblogs.com/baiqiantao/p/5583908.html