(一)如何使用Handler
大家好, 这是我第一次写博客,这次简单的介绍一下如何使用Handler完成线程之间的通信。
先说明一下为什么会用到线程之间的通信,这是因为Android中像关于网络这些比较费时的操作必须要在子线程中进行,而UI这种操作则是在主线程中进行(毕竟重活不能给老大干嘛,而子线程就是小弟)。但是问题来了,我在子线程拿到了资源了怎么跳到主线程中更新UI呢?所以今天介绍多线程中通信的其中一种方法——使用Handler进行通信。
//这是在主线程中更新UI
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Bitmap bitmap = (Bitmap) msg.obj;
urlImg.setImageBitmap(bitmap);
}
};
//子线程中的网络操作,这里面用的是okhttp开源框架获取图片的
new Thread(new Runnable() {
@Override
public void run() {
try {
Request request = new Request.Builder()
.url("https://www.baidu.com/img/bdlogo.png")
.build();
Response response = client.newCall(request).execute();
InputStream inputStream = response.body().byteStream();
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
Message message = Message.obtain();
message.obj=bitmap;
handler.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
来看一下上面的代码,想要建立Handler通信就需要new 一个Handler了,在子线程中我们已经获取到了bitmap这个资源了,我们需要一个Message对象用以发送,这个对象你可以直接new当然最好是像我上面那样使用Message.obtain()
,用这种方法它会判断有没有Messeage对象,如果有就不会创建了,对于内存是会优化的。然后通过message.obj=bitmap
将资源放到了Message里去了(这个obj你想放啥都可以),在子线程中handler.sendMessage(message)
,这是通过Handler发送了消息。
而在主线程中通过了Handler中的handleMessage(Message msg)
我们拿到了消息,并获取了消息中的对象,并更新了UI。
(二)简谈Handler机制
知道了怎么用了以后,我们再来简单的了解一下Handler的机制,至少知道它流程是啥样的。
这是在下手画的,觉得丑的同学就多担待点吧。
接下来分析这个流程图,在子线程中的Handler将消息发送出去了,而在主线程中消息队列则是收到了message并放到它这个队列里,而在主线中有一个叫Looper的东东是消息循环器,它可以不断循环的读取消息队列中的消息并获取,在下面的Looper类中的loop方法里可以看到有一个死循环在不断地读取,而读取的对象就是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) {
// 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();
}
}
在读取过后拿到了message,而在Looper类里面有这么一段代码msg.target.dispatchMessage(msg);
那么这个msg.target是什么呢?
我们在Message这个类里面可以找到
/*package*/ Handler target;
定义了一个Handler类的target,这说明了Looper拿到了消息以后是要给主线程中的Handler来处理的,然后我们继续找后面的dispatchMessage(msg)
,在Handler类中有这么一段代码
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这说明了消息被Handler接受后需要实现handleMessage(msg)
,在这个方法中我们可以拿到了message同时拿到了它所承载的对象并更新我们的UI。
大致的流程跟我上面手画的流程图是差不多的。