Handler Looper 原理 详解

时间:2023-03-08 21:54:21

核心知识点

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。
Handler Looper 原理 详解

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的全部方法

构造方法
Handler Looper 原理 详解
其他方法
Handler Looper 原理 详解
获取消息
Handler Looper 原理 详解
移除消息和回调
Handler Looper 原理 详解
发送消息
Handler Looper 原理 详解
post方法
Handler Looper 原理 详解

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

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("\n1、在UI线程中用子线程的Handler发消息,what=" + msg.what);
break;
case 2:
//其实这个Runnable并没有创建什么线程,而是发送了一条消息,当Handler收到此消息后回调run()方法
uiHandler.post(() -> tv_info.append("\n演示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("\n4、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("\n5、在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("\n2、子线程的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("\n3、在子线程中用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