Handler是Android中处理异步消息的机制。Looper、Handler、MessageQueue、Message概括来说就是:Looper负责的就是创建一个MessageQueue,然后进入一个无限循环体不断从该MessageQueue中读取消息Message,然后回调相应的消息处理函数,而消息的创建者就是一个或多个Handler,执行完成一个消息后则继续循环。
- MessageQueue详解:消息队列MessageQueue就是存放消息的队列。那队列中存储的消息是什么呢?假设我们在UI界面上单击了某个按钮,而此时程序又恰好收到了某个广播事件,那我们如何处理这两件事呢?因为一个线程在某一时刻只能处理一件事情,不能同时处理多件事情,所以我们不能同时处理按钮的单击事件和广播事件,我们只能挨个对其进行处理,只要挨个处理就要有处理的先后顺序。为此Android把UI界面上单击按钮的事件封装成了一个Message,将其放入到MessageQueue里面去,即将单击按钮事件的Message入栈到消息队列中,然后再将广播事件的封装成以Message,也将其入栈到消息队列中。也就是说一个Message对象表示的是线程需要处理的一件事情,消息队列就是一堆需要处理的Message的池。线程Thread会依次取出消息队列中的消息,依次对其进行处理。
- Looper详解:消息队列MessageQueue只是存储Message的地方,真正让消息队列循环起来的是Looper,这就好比消息队列MessageQueue是个水车,那么Looper就是让水车转动起来的河水,如果没有河水,那么水车就是个静止的摆设,没有任何用处。Looper让MessageQueue动了起来。
Looper是用于使线程中的消息循环起来的工具。在默认情况下,当我们创建一个新的线程时,这个线程内部是没有消息队列MessageQueue的。为了能够让线程绑定一个消息队列,我们需要借助Looper。首先,我们需要调用Looper的prepare()方法,然后调用Looper的loop()方法。
(一)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));
sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。可以看到,将一个Looper的实例放入了ThreadLocal,并且先判断了sThreadLocal.get是否为null,否则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例。
(二)构造函数
上面的代码执行了Looper的构造函数,我们看一下其代码:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); }
在构造函数中,创建了一个消息队列MessageQueue,并将其赋值给成员字段mQueue,这样Looper也就与MessageQueue通过成员字段mQueue进行了关联。
(三)loop()方法
当调用Looper的loop()方法时,它会不断地从消息队列MessageQueue中取出消息并处理。如果消息队列为空,那么Looper会阻塞当前线程,直到有新的消息到来。这样就实现了线程中的消息循环。
这段代码是一个在Android系统中用于消息循环的方法。以下是对代码的重构:
```java
public static void loop() {
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;
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.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();
}
}
```
这里对代码进行了格式化和简化,使其更易于阅读。主要改动包括:去掉了多余的空格,将长字符串拆分成多行以提高可读性,以及使用标准的Java命名规范。
以下是根据您提供的内容重构后的代码,并保持了段落结构:
```java
final Looper me = myLooper();
if (me == null) { throw new RuntimeException(“No Looper; Looper.prepare() wasn't called on this thread.”); }
public static Looper myLooper() { return sThreadLocal.get(); }
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
msg.target.dispatchMessage(msg);
msg.recycle();
}
```
Looper主要作用是与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。在这段代码中,myLooper()方法直接返回了sThreadLocal存储的Looper实例。如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。然后拿到该looper实例中的消息队列mQueue。进入了一个无限循环,每次取出一条消息,如果没有消息则阻塞。最后通过消息队列MessageQueue的next方法从消息队列中取出一条消息,如果此时消息队列中有Message,那么next方法会立即返回该Message,如果此时消息队列中没有Message,那么next方法就会阻塞式地等待获取Message。然后通过消息所关联的Handler通过dispatchMessage方法让Handler处理该Message,并释放消息占据的资源。
Handler详解
一、Handler的构造函数
在使用Handler之前,我们需要初始化一个实例。例如,如果我们想要在UI线程中更新UI,我们可以在声明时直接初始化Handler实例,或者在onCreate方法中初始化。因此,首先我们来看一下Handler的构造方法,了解它是如何与MessageQueue联系起来的,以及如何在子线程中发送消息(通常情况下,发送消息都在非UI线程)到MessageQueue中的。
二、loop()方法
loop()方法是Handler类的一个关键方法,它的作用是不断地从MessageQueue中取出消息,并将这些消息交给消息Message的target属性所指向的对象(即Handler的dispatchMessage方法去处理)。这样,我们就可以在子线程中处理消息,而不需要担心阻塞UI线程。
以下是重构后的代码,我将原始代码进行了调整以提高可读性:
```java
public Handler() {
}
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();
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;
}
```
重构的关键点包括:
1. 将构造方法中的逻辑拆分到单独的方法中,以提高代码可读性。
Handler.Callback是用于处理Message的一种方法。如果没有传递该参数,我们有两种方式使得Handler能够处理Message:
1. 向Handler的构造函数传入一个Handler.Callback对象,并实现Handler.Callback的handleMessage方法。
2. 不向Handler的构造函数传入Handler.Callback对象,但是需要重写Handler本身的handleMessage方法。
在主线程中,我们创建了一个名为mHandler的Handler实例,并自动绑定到主线程。这个Handler实例重写了handleMessage方法,用于处理消息。当收到消息时,它会根据消息的类型进行相应的操作。在这个例子中,当收到消息类型为1时,它会打印出当前线程的ID、消息的参数以及消息的数据。最后,它会将textview的文本设置为"success"。
```java
// 在主线程中创建mHandler,所以自动绑定主线程
private Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 1:
System.out.println("handleMessage thread id " + Thread.currentThread().getId());
System.out.println("msg.arg1:" + msg.arg1);
System.out.println("msg.arg2:" + msg.arg2);
System.out.println("msg.obj:" + msg.obj.toString());
System.out.println("msg.setDate:" + msg.getData().get("QQ"));
textview.setText("success");
break;
}
}
};
private Handler mHandler2 = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 1:
System.out.println("handleMessage thread id " + Thread.currentThread().getId());
System.out.println("msg.arg1:" + msg.arg1);
System.out.println("msg.arg2:" + msg.arg2);
System.out.println("msg.obj:" + msg.obj.toString());
System.out.println("msg.setDate:" + msg.getData().get("QQ"));
textview.setText("success");
return false;
}
return false;
}
});
```
在Android中,Handler类用于处理消息队列中的消息。为了实现handleMessage方法,我们需要通过某种方式来调用它。这与Java中对Thread的设计有异曲同工之处。
在Java中,如果我们想使用多线程,有两种方法:
1. 向Thread的构造函数传入一个Runnable对象,并实现Runnable的run方法。
2. 无需向Thread的构造函数传入Runnable对象,但是要重写Thread本身的run方法。
首先,我们需要获取当前线程保存的Looper实例。可以通过调用Looper.myLooper()方法来实现。然后,我们需要获取该Looper实例中保存的消息队列MessageQueue,这样就保证了Handler的实例与Looper实例中MessageQueue关联上了。
接下来,我们来看一下Handler类中的sendMessage()方法。这个方法有三个重载版本:
1. sendMessage(Message msg):将指定的消息发送到消息队列中。如果消息队列已满,则会将该消息放入消息队列的延迟投递队列中,等待一段时间后再发送。
2. sendEmptyMessageDelayed(int what, long delayMillis):发送一个空消息,但延迟一定时间后才发送。这里的delayMillis参数表示延迟的时间,单位为毫秒。
3. sendMessageDelayed(Message msg, long delayMillis):将指定的消息延迟一定时间后发送到消息队列中。这里的delayMillis参数同样表示延迟的时间,单位为毫秒。
总之,要实现handleMessage方法,我们需要通过某种方式调用它,这与Java中对Thread的设计有异曲同工之处。同时,Handler类提供了sendMessage()方法来处理消息队列中的消息,包括发送普通消息、空消息以及延迟发送消息等。
```java
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
if (mQueue == null) {
RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(msg, uptimeMillis);
}
private boolean enqueueMessage(Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return mQueue.enqueueMessage(msg, uptimeMillis);
}
```
Looper的工作原理是这样的:首先调用prepare()和loop()方法,在当前执行的线程中保存一个Looper实例。这个实例会保存一个MessageQueue对象,然后当前线程进入一个无限循环中去,不断从MessageQueue中读取Handler发来的消息。然后再回调创建该消息的Handler中的dispathMessage方法。
Handler的dispatchMessage方法如下:
```java
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
```
其中有几行代码是关键代码:
1. `if (msg.callback != null) { handleCallback(msg); }`
首先会判断msg.callback存不存在,msg.callback是Runnable类型,如果msg.callback存在,那么说明该Message是通过执行Handler的postXXX系列方法将Message放入到消息队列中的,这种情况下会执行handleCallback(msg), handleCallback源码如下:
```java
private static void handleCallback(Message message) {
message.callback.run();
}
```
这样我们就清楚地看到我们执行了msg.callback的run方法,也就是执行了postXXX所传递的Runnable对象的run方法。
2. `else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } }`
当msg.callback不存在时,会检查mCallback是否存在。如果mCallback存在并且处理了消息(通过调用handleMessage方法并返回true),则直接返回,不再继续后续处理。否则,会调用handleMessage(msg)处理消息。
如果我们没有通过postXXX系列方法将Message放入到消息队列中,那么msg.callback就是null。在这种情况下,代码会继续执行并判断Handler的成员字段mCallback是否存在。mCallback是Handler.Callback类型的,我们在Handler的构造函数中可以传递一个实现了handleMessage方法的Callback对象。如果我们在构造函数中传递了该Callback对象,那么我们会让Callback的handleMessage方法来处理Message。
当msg.callback为null时,我们会调用Handler自身的handleMessage方法。需要注意的是,这个方法默认是一个空方法,我们需要自己重写实现该方法。
综上所述,Handler提供了三种途径处理Message,并且处理有前后优先级之分:首先尝试让postXXX中传递的Runnable执行,其次尝试让Handler构造函数中传入的Callback的handleMessage方法处理,最后才是让Handler自身的handleMessage方法处理Message。
让我们看一下handleMessage(msg)方法:
```java
/** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { }
```
可以看到这是一个空方法,因为消息的最终回调是由我们控制的。在创建handler的时候,我们都会复写handleMessage方法,然后根据msg.what进行消息处理。
以下是重构后的代码:
```java
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case value:
break;
default:
break;
}
}
};
// sendMessage方式流程已经解释完毕,接下来看下post方式。
mHandler.post(new Runnable() {
@Override
public void run() {
Log.e("TAG", Thread.currentThread().getName());
mTxt.setText("yoxi");
}
});
```
在这段代码中,我们首先创建了一个名为`mHandler`的`Handler`对象。然后,我们使用`post()`方法将一个实现了`Runnable`接口的匿名类对象发送给`mHandler`。在`run()`方法中,我们可以编写更新UI的代码。实际上,这个`Runnable`并没有创建什么线程,而是发送了一条消息。下面是源码:
```java
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;
}
```
在getPostMessage方法中,首先会得到一个Message对象。然后将我们创建的Runable对象作为callback属性,赋值给这个Message对象。需要注意的是,创建Message对象时可以使用new或者Message.obtain()方法。虽然两者都可以实现相同的目的,但更推荐使用Message.obtain()方法,因为Message内部维护了一个消息池用于Message对象的复用,避免了每次都重新分配内存的问题。
sendMessageDelayed方法接收两个参数:一个是Message对象msg,另一个是延迟时间(毫秒)delayMillis。如果延迟时间小于0,则将其设置为0。然后调用sendMessageAtTime方法,将当前系统运行时间加上延迟时间作为参数传递给该方法。
sendMessageAtTime方法接收两个参数:一个是Message对象msg,另一个是系统运行时间uptimeMillis。在方法内部,首先获取消息队列(MessageQueue),然后判断消息队列是否为空。如果为空,则抛出一个RuntimeException异常,并记录日志。最后,调用enqueueMessage方法将消息加入消息队列。
最终的实现方式与handler.sendMessage相似,都是先调用sendMessageAtTime方法,然后调用enqueueMessage方法将消息加入消息队列。在这个过程中,需要为msg.target赋值为handler,以便在消息处理完成后能够找到对应的处理程序。
这段代码是一个名为`dispatchMessage`的方法,它接收一个`Message`类型的参数`msg`。该方法首先判断`msg.callback`是否为`null`,如果不为`null`,则调用`handleCallback(msg)`方法处理回调。如果为`null`,则执行以下操作:
1. 检查当前线程中是否已经存在一个Looper实例,如果存在且该实例中有MessageQueue对象,则将该MessageQueue赋值给成员变量`mQueue`;否则,创建一个新的MessageQueue对象并赋值给成员变量`mQueue`。
2. 如果当前线程中没有MessageQueue对象,则创建一个新的Thread类的子类,并在该子类的构造方法中初始化消息队列MessageQueue对象,并将其保存在成员变量`mQueue`中。然后调用父类的构造方法,将该子类的实例设置为当前线程的Looper。
3. 在Looper构造函数中,创建了一个消息队列MessageQueue,并将其赋值给其成员字段`mQueue`,这样Looper也就与MessageQueue通过成员字段`mQueue`进行了关联。
4. Looper.loop()会让当前线程进入一个无限循环,不断地从MessageQueue的实例中读取消息,然后回调`msg.target.dispatchMessage(msg)`方法。
5. Handler的构造方法会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。
6. Handler的sendMessage方法会给`msg.target`赋值为handler自身,然后加入到MessageQueue中。
7. 在构造Handler实例时,我们会重写handleMessage方法,也就是最终调用的方法:`msg.target.dispatchMessage(msg)`。
本文将探讨Thread、MessageQueue、Looper和Handler之间的关系,并通过一张传送带的图来形象地展示它们之间的联系。
在现实生活中的生产线上,我们可以看到各种各样的传送带,这些传送带上堆满了各种货物。当传送带开始运转时,它会在发动机滚轮的带动下不断向前滚动,同时在一端不断地放置新的货物。货物在传送带的带动下被送到另一端进行收集和处理。
我们可以将传送带上的货物视为一个个Message,而承载这些货物的传送带可以被视为消息队列MessageQueue。发送机滚轮(即Looper)负责驱动传送带转动,发动机的转动需要电源支持,因此电源可以被视为线程Thread。所有的消息循环操作都是基于某个线程进行的。
准备工作完成后,只需按下电源开关(即Looper的loop方法),发动机就会开始运转。当我们按下这个开关时,就相当于执行了Looper的loop方法,从而使消息队列开始循环。
那么,Handler在这个传送带模型中相当于什么呢?我们可以将Handler视为放置货物和取走货物的管道:货物从一端顺着管道进入传送带,然后从另一端顺着管道离开传送带。我们通过调用Handler的sendMessageXXX、sendEmptyMessageXXX或postXXX方法将Message对象放入消息队列MessageQueue中,这就像是在传送带的一端放入货物。当货物从传送带的另一端顺着管道离开时,我们相当于调用了Handler的dispatchMessage方法,最后执行handleMessage方法对Message进行处理。
文章转自:https://juejin.cn/post/6844904195653386248,如有侵权,请联系删除。