源码解析handler机制

作者: 辩护人 | 来源:发表于2019-07-10 17:01 被阅读23次

    handler机制是Android重要的多线程数据传输机制,所以想从源码来解析这个机制。

    一般使用

    在Activity中

    public class MainActivity extends AppCompatActivity {
    
        Handler handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                switch (msg.what){
                    case 1:
                        showToast((String) msg.obj);
                        break;
                }
                return false;
            }
        });
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button button = findViewById(R.id.btn_start);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            Message message = Message.obtain();
                            message.what = 1;
                            message.obj = "Handler机制";
                            handler.sendMessage(message);
                        }
                    }).start();
                }
            });
        }
    
        private void showToast(String content){
            Toast.makeText(this,content,Toast.LENGTH_SHORT).show();
        }
    }
    

    Handler机制

    1 最基本的Message

    Message两种创建

    • 直接构造
    Message message = new Message();
    
    • 通过obtain()方法
        /**
         * Return a new Message instance from the global pool. Allows us to
         * avoid allocating new objects in many cases.
         */
        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();
        }
    

    注释很清楚,从全局池返回一个Message实例,避免在很多情况下分配新对象

    很重要的变量

    Handler target;
    Message next;
    

    这两个之后会用到

    2 handler.sendMessage方法

        public final boolean sendMessage(Message msg)
        {
            return sendMessageDelayed(msg, 0);
        }
    

    这些相关的sendMessage方法最终会调用一个enqueueMessage方法

        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    这个方法中将Messagetarget赋值为handler自己,并调用了MessageQueue.enqueueMessage方法

    3 MessageQueue.enqueueMessage方法

        boolean enqueueMessage(Message msg, long when) {
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
    
            synchronized (this) {
                if (mQuitting) {
                    IllegalStateException e = new IllegalStateException(
                            msg.target + " sending message to a Handler on a dead thread");
                    Log.w(TAG, e.getMessage(), e);
                    msg.recycle();
                    return false;
                }
    
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {
                    // New head, wake up the event queue if blocked.
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                    // Inserted within the middle of the queue.  Usually we don't have to wake
                    // up the event queue unless there is a barrier at the head of the queue
                    // and the message is the earliest asynchronous message in the queue.
                    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;
                }
    
                // We can assume mPtr != 0 because mQuitting is false.
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }
    
    

    核心大概这些代码,其实就是一个单向链表

        boolean enqueueMessage(Message msg, long when) {
            //保证线程安全性
            synchronized (this) {
                
                msg.when = when;
                Message p = mMessages;
                //这个消息是第一个或这个消息无需延迟 或 这个消息延迟时间比第一个消息短
                if (p == null || when == 0 || when < p.when) {
                    //把这个消息放到链表的头
                    msg.next = p;
                    mMessages = msg;
                   
                } else {
                    //循环整个链表,直到结束或这个消息延迟时间比某一个消息短
                    // 1. 直到结束情况:把这个消息放到链表的末尾
                    // 2.这个消息延迟时间比某一个消息短: prev -> p 改为 prev -> msg -> p
                    Message prev;
                    for (;;) {
                        prev = p;
                        p = p.next;
                        if (p == null || when < p.when) {
                            break;
                        }
                    }
                    msg.next = p; // invariant: p == prev.next
                    prev.next = msg;
                }
            }
            return true;
        }
    
    

    本质上完成对于把你传递的消息跟之前的消息排序

    4. Looper的loop方法(循环消息队列)

        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();
            }
        }
    
    

    其实核心就干了一件事msg.target.dispatchMessage(msg);

        public static void loop() {
            final Looper me = myLooper();
            final MessageQueue queue = me.mQueue;
     
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
    
                try {
                    msg.target.dispatchMessage(msg);
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
            }
        }
    

    诡异的是这个和我们最开始Handler的实现的回调方法名并不是一样的

    5.Handler的handleMessage方法

    原来最后是由dispatchMessage方法调用的,其实也是因为两种方式来实现handleMessage,一种通过匿名接口回调实现,另外一种直接重写handleMessage

        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    
    

    一些疑问

    Handler与Looper的关联

        public Handler(Callback callback, boolean async) {
            if (FIND_POTENTIAL_LEAKS) {
                final Class<? extends Handler> klass = getClass();
                if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                        (klass.getModifiers() & Modifier.STATIC) == 0) {
                    Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                        klass.getCanonicalName());
                }
            }
    
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    所以的构造都会调用这个,在这个里面调用获取了mLooper和mQueue

    Looper的创建

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
        private static void prepare(boolean quitAllowed) {
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            sThreadLocal.set(new Looper(quitAllowed));
        }
    
    

    通过ThreadLocal的特性,保证每个线程只存在一个Looper对象。

    ThreadLocal是一个关于创建线程局部变量的类。
    通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。

    而这个实在ActivityThread(并没有继承线程)中被调用prepareMainLooper方法

    public static void main(String[] args) {
            ...
            Looper.prepareMainLooper();
            ...
    

    Handler 为什么可以 post Runnable

    其实就是Handler自己帮你把Runnable转换成Message

        private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r;
            return m;
        }
    

    子线程展示Toast报错的原因

    很多人都以为子线程展示Toast是因为不在主线程,其实并不是。

    private static class TN extends ITransientNotification.Stub {
    ...
    TN(String packageName, @Nullable Looper looper) {
                ...
    
                if (looper == null) {
                    // Use Looper.myLooper() if looper is not specified.
                    looper = Looper.myLooper();
                    if (looper == null) {
                        throw new RuntimeException(
                                "Can't toast on a thread that has not called Looper.prepare()");
                    }
                }
                mHandler = new Handler(looper, null) {
                    @Override
                    public void handleMessage(Message msg) {
                        switch (msg.what) {
                            case SHOW: {
                                IBinder token = (IBinder) msg.obj;
                                handleShow(token);
                                break;
                            }
                            case HIDE: {
                                handleHide();
                                // Don't do this in handleHide() because it is also invoked by
                                // handleShow()
                                mNextView = null;
                                break;
                            }
                            case CANCEL: {
                                handleHide();
                                // Don't do this in handleHide() because it is also invoked by
                                // handleShow()
                                mNextView = null;
                                try {
                                    getService().cancelToast(mPackageName, TN.this);
                                } catch (RemoteException e) {
                                }
                                break;
                            }
                        }
                    }
                };
            }
    }
    

    因为Toast需要初始化一个Handler,并且最后的show仍然是在Handler的回调执行的
    同理,子线程展示Dialog报错也是因为这个
    如果非要在子线程展示Toast,可以这样

     class LooperThread extends Thread {
            public Handler mHandler;
      
            public void run() {
               Looper.prepare();
               Toast.makeText(getApplicationContext(),"子线程展示Toast",Toast.LENGTH_SHORT).show();
               Looper.loop();
           }
      }
    

    但是不要这么做,因为Looper会阻塞这个线程

    主线程不会因为Looper.loop()里的死循环卡死

    这里涉及线程,先说说说进程/线程,进程:每个app运行时前首先创建一个进程,该进程是由Zygote fork出来的,用于承载App上运行的各种Activity/Service等组件。进程对于上层应用来说是完全透明的,这也是google有意为之,让App程序都是运行在Android Runtime。大多数情况一个App就运行在一个进程中,除非在AndroidManifest.xml中配置Android:process属性,或通过native代码fork进程。
    线程:线程对应用来说非常常见,比如每次new Thread().start都会创建一个新的线程。该线程与App所在进程之间资源共享,从Linux角度来说进程与线程除了是否共享资源外,并没有本质的区别,都是一个task_struct结构体,在CPU看来进程或线程无非就是一段可执行的代码,CPU采用CFS调度算法,保证每个task都尽可能公平的享有CPU时间片。
    有了这么准备,再说说死循环问题:
    对于线程既然是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,例如,binder线程也是采用死循环的方法,通过循环方式不同与Binder驱动进行读写操作,当然并非简单地死循环,无消息时会休眠。但这里可能又引发了另一个问题,既然是死循环又如何去处理其他事务呢?通过创建新线程的方式。
    真正会卡死主线程的操作是在回调方法onCreate/onStart/onResume等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop本身不会导致应用卡死。
    转载Gityuan的知乎回答

    也就是说looper本身不会,它只是分发消息。ANR只是出现在处理消息的时候(也就是我们触摸屏幕等)

    总结handler机制流程图

    最后整个机制可以用这个流程图表示


    handler机制流程图.png

    相关文章

      网友评论

        本文标题:源码解析handler机制

        本文链接:https://www.haomeiwen.com/subject/tuuahctx.html