Handler时Android消息机制的上层接口,通过它可以轻松地将一个任务切换到Handler所在的线程中去执行。
Android的消息机制指的是Handler的运行机制。
本质上说,Handler不是专门用来更新UI的,它只是常被开发者用来更新UI。
Handler的运行需要底层的MessageQueue和Looper的支撑。
※ Handler:负责Message的发送及处理。
1、Handler.sendMessage():向消息池发送各种消息事件。
2、Handler.handleMessage() :处理相应的消息事件。
※ Message:需要被传递的消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,最终由Handler处理。
※ MessageQueue:用来存放Handler发送过来的消息,内部通过单链表的数据结构来维护消息列表,等待Looper的抽取。
※ Looper:通过Looper.loop()无限循环地从MessageQueue中抽取Message,按分发机制将消息分发给目标处理者。
ActivityThread,主线程,被创建时就会初始化Looper,这也是在主线程中默认可以使用Handler的原因,详见如下源码: Looper.prepareMainLooper();
public final class ActivityThread {
public static final String TAG = "ActivityThread";
final Looper mLooper = Looper.myLooper();
public static void main(String[] args) {
// ......
Looper.prepareMainLooper();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
// ......
}
}
10.1 Android的消息机制概述
开发过程我们中比较多地接触Handler,Handler的主要作用是将一个任务切换到某一个指定的线程。
Android不允许在子线程中更新UI,所以需要Handler。
private TextView tvText;
private Button btnChange;
private static final int UPDATE_TEXT = 1;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
// 主线程处理
switch (msg.what){
case UPDATE_TEXT:
tvText.setText("Nice to meet you");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvText = findViewById(R.id.tv_text);
btnChange = findViewById(R.id.btn_change);
btnChange.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_change:
new Thread(new Runnable() {
@Override
public void run() {
// 子线程发送Message
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message);
}
}).start();
break;
default:
break;
}
}
代码分析:点击事件开启了Thread,创建了Message,介者调用handler.sendMessage(message),然后在handleMessage()方法中处理,显然handleMessage()是在主线程中执行。
handler.png
10.2 Android的消息机制分析
10.2.1 ThreadLocal的工作原理
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取存储的数据,对于其他线程来说则无法获取到数据。
private ThreadLocal<Boolean> mBooleanThreadLocal = new ThreadLocal<Boolean>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBooleanThreadLocal.set(true);
Log.d("MainActivity", "onCreate: "+"[Thread#main]"+mBooleanThreadLocal.get());
new Thread("Thread#1"){
@Override
public void run() {
mBooleanThreadLocal.set(false);
Log.d("MainActivity", "onCreate: "+"[Thread#1]"+mBooleanThreadLocal.get());
}
}.start();
new Thread("Thread#2"){
@Override
public void run() {
Log.d("MainActivity", "onCreate: "+"[Thread#2]"+mBooleanThreadLocal.get());
}
}.start();
}
输出结果:
2019-08-29 13:58:23.649 5983-5983/? D/MainActivity: onCreate: [Thread#main]true
2019-08-29 13:58:23.652 5983-6008/? D/MainActivity: onCreate: [Thread#1]false
2019-08-29 13:58:23.652 5983-6009/? D/MainActivity: onCreate: [Thread#2]null
从日志可以看出,虽然在不通线程中访问的是同一个ThreadLocal对象,但是它们通过ThreadLocal获取到的值却是不一样。我们来阅读ThreadLocal.java中的set()和get()方法。
public void set(T value) {
// 获取当前的线程
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
// 往map里面set值
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
public T get() {
// 获取当前的线程
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
// 获取map里面的值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
总结:ThreadLocal可以在多个线程中互不干扰地存储和修改数据。
10.2.2 消息队列的工作原理
消息队列是指Android中的MessageQueue,主要包含
1、插入:enqueueMessage()
2、读取:next()
两个方法。事实上她是通过单链表数据结构来维护消息列表,enqueueMessage主要操作就是单链表的插入操作。
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
...
}
return true;
}
next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法将一直阻塞在这里。
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
...
}
...
}
}
10.2.3 Looper的工作原理
Looper在Android的消息机制中扮演着消息循环的角色,具体来说就是它会不停地从MessageQueue中查看是否有新消息,如果有新消息就会立即处理,否则就一直阻塞在那里。
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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
for (;;) { // 死循环
Message msg = queue.next(); // might block
if (msg == null) { // 唯一的跳出循环的条件,MessageQueue.next() == 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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", 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();
}
}
10.2.4 Handler的工作原理
Handler的工作主要包含消息的发送和接收过程:
1、发送:post()系列和send()系列,核心方法sendMessage(Message msg)
2、接收:dispatchMessage(Message msg)
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
10.3 主线程的消息循环
Android的主线程就是ActivityThread。
核心代码两行:
Looper.prepareMainLooper(); // 创建主线程的Looper以及MessageQueue;
Looper.loop(); // 开启主线程的消息循环。
public static void main(String[] args) {
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
Security.addProvider(new AndroidKeyStoreProvider());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper(); // 创建主线程的Looper以及MessageQueue
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop(); // 开启主线程的消息循环
throw new RuntimeException("Main thread loop unexpectedly exited");
}
ActivityThread内部定义了H,用来和消息队列交互。
private class H extends Handler {
// 开启活动
public static final int LAUNCH_ACTIVITY = 100;
// 暂定活动
public static final int PAUSE_ACTIVITY = 101;
// ...省略
String codeToString(int code) {
if (DEBUG_MESSAGES) {
switch (code) {
case LAUNCH_ACTIVITY: return "LAUNCH_ACTIVITY";
case PAUSE_ACTIVITY: return "PAUSE_ACTIVITY";
// ...省略
}
}
return Integer.toString(code);
}
}
ApplicationThread向H发送消息,H收到消息后会将ApplicationThread中的逻辑切换到ActivityThread中去执行,即切换到主线程去执行,这个过程就是主线程的消息循环模型。
网友评论