(一)如何使用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的机制,至少知道它流程是啥样的。

android ui线程通讯 安卓线程通讯_Android


这是在下手画的,觉得丑的同学就多担待点吧。

接下来分析这个流程图,在子线程中的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。
大致的流程跟我上面手画的流程图是差不多的。