Android中线程之间的通信——Handler
(一)如何使用Handler
大家好, 这是我第一次写博客,这次简单的介绍一下如何使用Handler完成线程之间的通信。
先说明一下为什么会用到线程之间的通信,这是因为Android中像关于网络这些比较费时的操作必须要在子线程中进行,而UI这种操作则是在主线程中进行(毕竟重活不能给老大干嘛,而子线程就是小弟)。但是问题来了,我在子线程拿到了资源了怎么跳到主线程中更新UI呢?所以今天介绍多线程中通信的其中一种方法——使用Handler进行通信。
//这是在主线程中更新UIHandler 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。
大致的流程跟我上面手画的流程图是差不多的。
更多相关文章
- Handler学习
- Android(安卓)一个可以自由定制外观、支持拖拽消除的MaterialDes
- Android(安卓)Handler 四个使用实例 及HandlerThread的使用
- android 进程与线程 相关
- Android异步任务处理之Thread线程
- 在Android中为啥建议你用Message.obtain()方法获取Message对象,而
- android中,由于图像处理不当而引起的OOM问题及其解决方案(一)
- 在ListView中增加HeaderView和FootView
- 线程对象Android(安卓)开发之多线程处理、Handler 详解