写在前面:
每每看到大佬,默默留下没技术的眼泪。
好在最近比较清闲,打算写几篇基础类的文章;弥补一些知识点的不足,也希望可以帮助到其他小伙伴。
文章将以小白的视角,对自己所理解的内容进行解读,统称为小白系列吧。
至于内容,多半是东拼西凑;至于错误及不足,希望大家予以指出;参考博文在文末予以奉上(比心)。
一,我是谁:Handler是什么?为什么要有Handler?
Handler是什么?
- 简单说,Handler就是解决线程和线程之间的通信的一种机制。
为什么需要Handler呢?
- 因为Android是单线程模型,只允许在UI线程更新UI操作。
- 而一些耗时操作(如访问网络、数据库等操作),是需要交由子线程去执行的。
- 但子线程并不能进行UI的更新,需要使用Handler来解决线程与线程之间的通讯。
至于为什么Android使用单线程模型:
因为如果任意线程都可以更新UI的话,在多线程并发访问情况下,线程安全问题处理起来会相当麻烦复杂。虽然可以通过加锁进行解决,但降低 UI 访问的效率。
二,到哪里去:Handler的基本使用
- Handler的基本使用可以分为两大类:在主线程的使用,和在子线程的使用。
这里先介绍Handler的正确使用,至于为什么这么使用,以及细节需要注意,我们将在后面的源码解析部分进行探讨。
在主线程使用Handler,示例如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "testHandler ---->";
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//获得刚才发送的Message对象,然后在这里进行UI操作
Log.e(TAG, "msg.what = " + msg.what);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
testHandler();
}
private void testHandler() {
//开启一个线程模拟处理耗时的操作
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
//通过Handler发送一个消息切换回主线程(mHandler所在的线程)
mHandler.sendEmptyMessage(0);
}
}).start();
}
}
执行结果:
E/testHandler ---->: msg.what = 0
在主线程使用handler很简单,只需在主线程创建一个handler对象,在子线程通过在主线程创建的handler对象发送Message,在handleMessage()方法中接受这个Message对象进行处理。通过handler很容易的从子线程切换回主线程了。
在子线程的使用Handler,示例如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "testHandler ---->";
@SuppressLint("HandlerLeak")
//主线程中的handler
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//获得刚才发送的Message对象,然后在这里进行UI操作
Log.e(TAG, "msg.what = " + msg.what);
}
};
//子线程中的handler
private Handler mHandlerThread = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
testHandler();
}
private void testHandler() {
//开启一个线程模拟处理耗时的操作
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
//通过Handler发送一个消息切换回主线程(mHandler所在的线程)
mHandler.sendEmptyMessage(0);
//调用Looper.prepare()方法
Looper.prepare();
mHandlerThread = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e("sub thread ---->", "msg.what = " + msg.what);
}
};
mHandlerThread.sendEmptyMessage(1);
//调用Looper.loop()方法
Looper.loop();
}
}).start();
}
}
执行结果:
E/testHandler ---->: msg.what = 0
E/sub thread ---->: msg.what = 1
我们会发现,在子线程使用Handler需要调用Looper.prepare(),以及Looper.loop()方法。为何主线程这么流弊,可以省略步骤?答案后面揭晓。。
三,亲友团:Handler和ta的小伙伴们
首先我们先来了解一下Handler有哪些小伙伴,分别有哪些作用。
Handler的消息处理主要有五个部分组成:Message,Handler,Message Queue,Looper和ThreadLocal。
我们可以先看看图,对ta们做出一些简单的了解:
![](https://img.haomeiwen.com/i10346127/61fe88f464eca642.png)
-
Handler:主要是用于发送和处理消息的发送消息。一般使用sendMessage()方法,还有其他的一系列sendXxx的方法,但最终都是调用了sendMessageAtTime()方法,除了sendMessageAtFrontOfQueue()这个方法。
而发出的消息经过一系列的辗转处理后,最终会传递到Handler的handleMessage()方法中。 -
Message:是在线程之间传递的消息,它可以在内部携带少量的数据,用于线程之间交换数据。Message有四个常用的字段,what字段,arg1字段,arg2字段,obj字段。what,arg1,arg2可以携带整型数据,obj可以携带object对象。
-
Message Queue:是消息队列的意思,它主要用于存放所有通过Handler发送的消息,这部分的消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。
-
Looper:每个线程通过Handler发送的消息都保存在,MessageQueue中,Looper通过调用loop()的方法,就会进入到一个无限循环当中,然后每当发现Message Queue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中只会有一个Looper对象。
-
ThreadLocal:MessageQueue对象,和Looper对象在每个线程中都只会有一个对象,怎么能保证它只有一个对象,就通过ThreadLocal来保存。Thread Local是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储到数据,对于其他线程来说则无法获取到数据。
了解了这些基本概念后,我们深入源码来了解Handler的工作机制。
四,从哪里来:Handler的源码解析
Looper篇
每个应用程序都有一个入口,而Android程序是基于Java的,Java的程序入口是静态的main函数,因此Android程序的入口也应该为静态的main函数。
在Android程序中这个静态的main在ActivityThread类中。Java层的启动就是从这里开始的,我们来一起看一下:
public static void main(String[] args) {
。。。迷人的省略号。。。。
//有点儿类似prepare的方法
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
//这里也出现了loop的方法了
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
既然我们觉得有点像,那我们就来来看一下Looper.prepareMainLooper(),与Looper.prepare()方法:
public static void prepareMainLooper() {
//这里是false。说明该线程中的messageQueue是不可以被销毁的。
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//prepare里面执行set方法。这里执行get方法获取Looper
sMainLooper = myLooper();
}
}
public static void prepare() {
//这里是ture。说明该线程中的messageQueue可以被销毁的。
prepare(true);
}
通过对比,我们发现他们调用了prepare的重载方法。所以我们明白了,子线程使用Handler需要调用Looper.prepare(),以及Looper.loop()方法,而主线程中使用不需要,因为主线程在执行的时候已经调用。
然后我们看一下prepare的重载方法,Looper.prepare(boolean quitAllowed):
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));
}
在这里可以看出,sThreadLocal对象保存了一个Looper对象,首先判断是否已经存在Looper对象了,以防止被调用两次。sThreadLocal对象是ThreadLocal类型,因此保证了每个线程中只有一个Looper对象。Looper对象是什么创建的,我们进入看看,如下:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看出,这里在Looper构造函数中创建出了一个MessageQueue对象和保存了当前线程。从上面可以看出一个线程中只有一个Looper对象,而Message Queue对象是在Looper构造函数创建出来的,因此每一个线程也只会有一个MessageQueue对象。
然后我们再来看一下Looper.loop()都干了些什么:
public static void loop() {
//获取Looper
final Looper me = myLooper();
if (me == null) {
//如果me为null则抛出异常,也就是说loop方法必须在prepare方法之后运行。
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//获取该Looper下的queue(消息队列)
final MessageQueue queue = me.mQueue;
。。。迷人的省略号。。。。
//死循环。据说之前是while(true),反正就是不让你跳出去
for (;;) {
//取出一条消息,如果没有消息则阻塞。
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
。。。迷人的省略号。。。。
//这个方法很重要,其实就是把消息交给msg的handler对象去处理
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.recycleUnchecked();
}
}
所以说,Looper的主要作用:
1,与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
2,loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。
于是,我们有了消息队列MessageQueue,也有了在死循环中取消息的Looper,现在还缺少的就是谁去发送消息,于是乎今天的主角登场啦~
Handler篇
首先,我们先来了解一下Handler的构造方法:
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
。。。又是迷人的省略号。。。。
//这里获取一个mLooper
mLooper = Looper.myLooper();
if (mLooper == null) {
//如果mLooper为null抛出异常
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//获取mLooper当中的消息队列
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
我们可以看出,在使用new Handler()之前必须要有一个mLooper这个对象。不然的话会抛出异常。
如果有Looper实例的话,将获取了当前线程保存的Looper实例,并且获取了这个Looper实例中保存的MessageQueue(消息队列),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。
Handler的工作主要包含发送和接收过程。消息的发送主要通过post和send的一系列方法,而post的一系列方法是最终是通过send的一系列方法来实现的,我们来一起看一下常用的send方法:
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what){
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
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);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public final boolean sendMessageAtFrontOfQueue(Message msg) {
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, 0);
}
通过观察,除了sendMessageAtFrontOfQueue(),其余的方法最终调用了sendMessageAtTime,在此方法内部有直接获取MessageQueue然后调用了enqueueMessage方法,我们再来看看此方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//将当前的Handler作为msg的target属性
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//将消息插入到消息队列当中
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看出,handler将自身作为mag的target属性,并且将消息插入到消息队列当中。
在前面说到的Looper.loop()方法中,有个重要的msg.target.dispatchMessage(msg)方法,由于target就是当前的handler,所以处理消息还是交给msg的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);
}
}
而handleMessage(msg)方法是个空方法:
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)最终调用的方法。
最后奉上的各位大佬的博文(再度比心):
鸿洋大佬的: Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
另一位大佬的:Handler运行机制
还一位大佬的:彻底理解handler的实现原理
网友评论