手记

android开发经验谈:你到底有多了解handler

记早年的一次面试:

面试官:说说你对Handler的了解?

我:new 一个handler,然后在某个线程里发送message,在handler的handleMessage里接收message,message不要new 用Message.obtain()获取池子里的,来避免新建。

面试官:就这么多了么?

我:。。。。。。。。。。。。。。。。。

以前我对handler的理解很薄,在开发多年,深入了解多年后,细细的了解了handler

一、Looper

looper是关联message与handler的重要部分。

你在Android的子线程中new handler为啥报错,和looper关系不浅,据如下个例子,在子线程中调用handler

new Thread(new Runnable() {  
            public void run() {  
                Looper.prepare();  
                Handler handler = new Handler(){  
                    @Override  
                    public void handleMessage(Message msg) {  
                        Toast.makeText(getApplicationContext(), "handler msg", Toast.LENGTH_LONG).show();  
                    }  
                };  
                handler.sendEmptyMessage(1);  
                Looper.loop();  
            };  
        }).start();

image.gif

或者在子线程直接使用Looper.getMainLooper();

new Thread(new Runnable() {  
            public void run() {  
                Handler handler = new Handler(Looper.getMainLooper()){ 
                    @Override  
                    public void handleMessage(Message msg) {  
                        Toast.makeText(getApplicationContext(), "handler msg", Toast.LENGTH_LONG).show();  
                    }  
                };  
                handler.sendEmptyMessage(1);  
            };  
        }).start();

image.gif

Looper.getMainLooper()实际就是主线程的looper,主线程默认都会创建looper

创建looper时会创建messagequeue

注:每一个线程只能有一个looper,looper是线程持有

Looper实际上是给当前子线程建立的looper,为了能够遍历当前子线程的消息队列,

源码

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();        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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;            final long traceTag = me.mTraceTag;            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();            final long end;            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }            if (slowDispatchThresholdMs > 0) {                final long time = end - start;                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }            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();
        }
    }

image.gif

二、MessageQueue

MessageQueue是一个消息队列,用于存放message,looper来把其中的message发送到handlermessage

采用先进先出的方式来管理message,内部采用单链表来进行维护,插入删除比较快

三、Message

就是咱最常用的消息了,按照我上面的说法建议使用Message.obtain()来获取Message实例,应为message有个pool

源码

public static Message obtain() {        synchronized (sPoolSync) {            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;                return m;
            }
        }        return new Message();
    }



作者:王二蛋和他的狗
链接:https://www.jianshu.com/p/4b794294687b


0人推荐
随时随地看视频
慕课网APP