美文网首页我爱编程
Handler运行原理

Handler运行原理

作者: 我默默 | 来源:发表于2018-05-26 18:01 被阅读0次

    出现背景:

    为了方便线程之间的通讯。比如:主线程处理耗时操作就会引起卡顿ANR(Activity Not Respondinig),这是我们就要在子线程中去做耗时操作,通过Handler去通知主线程更新。

    用法简介

    接受消息方式

    1.在主线程中创建一个匿名内部类Handler,并且重写handleMessage方法。

    2直接去实现Handler里的CallBack接口,Handler通过回调CallBack接口里的handleMessage方法从而实现对UI的操作.

    发消息的方式。

    sendEmptyMessage(int what):发送空消息

    sendEmptyMessageDelayed(int what,long delayMillis):指定延时多少毫秒后发送空信息

    sendMessage(Message msg):立即发送信息

    sendMessageDelayed(Message msg):指定延时多少毫秒后发送信息

    Handler工作过程

    1在一个Android应用启动的时候,会创建一个主线程,就是ActivityThread。在ActivityThread中有一个静态的main方法为程序入口。再次方法中首先调用Looper.prepareMainLooper()函数。

    public static void main(String[] args) {

        ......

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();

        thread.attach(false);

        ......

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");

    }

    1从Looper构造方法中,创建了一个消息队列MessageQueue。

    private Looper(boolean quitAllowed) {

        mQueue = new MessageQueue(quitAllowed);

        mThread = Thread.currentThread();

    }

    1.1并且保存当前线程的对象Thread.currentThread()。但是构造方法私有,只有prepare方法中可以看出,ThreadLocal 中取出,如下:

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

    }

    在这里新建了一个Looper对象,然后保存在ThreadLocal中,当我们下次需要用到Looper的之后直接从这个sThreadLocal中取出即可。ThreadLocal类它实现了本地变量存储,我们将当前线程的数据存放在ThreadLocal中,若是有多个变量共用一个ThreadLocal对象,这时候在当前线程只能获取该线程所存储的变量,而无法获取其他线程的数据。在Looper这个类中为我们提供了myLooper来获取当前线程的Looper对象。从上面的方法还能够看出,一个线程只能创建一次Looper对象。

    2通过prepareMainLooper方法内部,先调用prepare(false)声明主线程不能终止消息循环,子线程可以

    public static void prepareMainLooper() {

        prepare(false);

        synchronized (Looper.class) {

            if (sMainLooper != null) {

                throw new IllegalStateException("The main Looper has already been prepared.");

            }

            sMainLooper = myLooper();

        }

    }

    接下来看一下main方法Looper.loop()做了事情。

    public static void loop() {

    // xxxxx==============>1获取当前线程中的Looper,并从Looper中获得消息队列

        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.

    //xxxxx==============> 确保这个线程的身份是本地进程的,并且与真实的token保持联系(这里只翻译下注释)

    Binder.clearCallingIdentity();

        final long ident = Binder.clearCallingIdentity();

    // xxxxx==============>所谓的死循环

        for (;;) {

    // xxxxx==============>从消息队列中取出消息,并且只有当取出的消息为空的时候才会跳出循环。

            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        Printer logging = me.mLogging;

    // xxxxx==============> 必须在UI线程记录系统日志,我们不用管直接跳过

            if (logging != null) {

                logging.println(">>>>> Dispatching to " + msg.target + " " +

                        msg.callback + ": " + msg.what);

            }

    // xxxxx==============> 将消息重新交由Handler处理。

            msg.target.dispatchMessage(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.     

        // xxxxx==============>  确保调用过程中线程没有被销毁。 (这里只翻译下注释)

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

            }

          // xxxxx==============> 对消息进行回收处理。 

            msg.recycleUnchecked();

        }

    }

    其实这里我看的不太明白,黑体字是我说一下自己此时的理解。

    msg.target是发送消息的Handler,通过Handler中的dispatchMessage方法又将消息交由Handler处理。消息处理完成之后便对消息进行回收处理。

    MessageQueue

     MessageQueue翻译为消息队里,在这个消息队列中是采用单链表的方式实现的,提高插入删除的效率。对于MessageQueue在这里我们也只看一下它的入队和出队操作。 这个我暂时理解的不是很好,暂时不做解释,以后补充。

    消息入队:

    boolean enqueueMessage(Message msg, long when) {

        ......

        synchronized (this) {

            ......

            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.           

              //  xxxxx==============>  新的消息,如果阻塞的话,唤醒事件队列。

               msg.next = p;

               mMessages = msg;

                needWake = mBlocked;

            } else {

                //1 Inserted within the middle of the queue  . 

           // 2 Usually we don't have to wake up the event queue unless there is a barrier at the head of the queue 

        // 3and the message is the earliest asynchronous message in the queue.   

    //  xxxxx==============>  

    //  1若是在中间插入,3则根据Message创建的时间进行插入  ,2并且这时消息队列如果处于等待状态的话则将其唤醒

    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;

    }

    消息出队(一脸懵逼,还望赐教)这里我就不BB了,希望大神赐教:

    Message next() {

        ......    int nextPollTimeoutMillis = 0;

        for (;;) {

            if (nextPollTimeoutMillis != 0) {

                Binder.flushPendingCommands();

            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {

                // Try to retrieve the next message.  Return if found.            final long now = SystemClock.uptimeMillis();

                Message prevMsg = null;

                Message msg = mMessages;

                if (msg != null && msg.target == null) {

                    // Stalled by a barrier.  Find the next asynchronous message in the queue.                do {

                        prevMsg = msg;

                        msg = msg.next;

                    } while (msg != null && !msg.isAsynchronous());

                }

                if (msg != null) {

                    if (now < msg.when) {

                        // Next message is not ready.  Set a timeout to wake up when it is ready.                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);

                    } else {

                        // Got a message.                    mBlocked = false;

                        if (prevMsg != null) {

                            prevMsg.next = msg.next;

                        } else {

                            mMessages = msg.next;

                        }

                        msg.next = null;

                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);

                        msg.markInUse();

                        return msg;

                    }

                } else {

                    // No more messages.                nextPollTimeoutMillis = -1;

                }

                // Process the quit message now that all pending messages have been handled.            if (mQuitting) {

                    dispose();

                    return null;

                }

                ......        }

            .....    }

    }

    退出队列的方法

    void quit(boolean safe) {

    //  xxxxx==============>  主线程的消息队列是不允许被退出的 mark。

        if (!mQuitAllowed) {

            throw new IllegalStateException("Main thread not allowed to quit.");

        }

        synchronized (this) {

    //  xxxxx==============>  mQuitting设为true可以退出消息队列 mark。

            if (mQuitting) {

                return;

            }

            mQuitting = true;

            if (safe) {

                removeAllFutureMessagesLocked();

            } else {

                removeAllMessagesLocked();

            }

            // We can assume mPtr != 0 because mQuitting was previously false.        nativeWake(mPtr);

        }

    }

    Handler

    窝草!终于翻篇了。

    Handler构造方法

    public Handler(Callback callback, boolean async) {

        ......

    //  xxxxx==============>  一个线程想创建Handler必须Looper.prepare() ,否则直接异常。

        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;

    }

    sendMessage的最终调用方法:

      public boolean sendMessageAtTime(Message msg, long uptimeMillis) {

            MessageQueue queue = mQueue;

            if (queue == null) {

                RuntimeException e = new RuntimeException(

                        this + " sendMessageAtTime() called with no mQueue");

                Log.w("Looper", e.getMessage(), e);

                return false;

            }

            return enqueueMessage(queue, msg, uptimeMillis);

        }

    然后看一下enqueueMessage方法

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

        msg.target = this;

        if (mAsynchronous) {

            msg.setAsynchronous(true);

        }

        return queue.enqueueMessage(msg, uptimeMillis);

    }

    从上边看出,Handler 通过sendMessage向MessageQueue中插入一条消息,这个MessageQueue是有Looper创建的(Looper的构造方法中),三者的主要连接桥梁就是Looper ,而且Looper和线程又是对应的,所以这四个的关系就是一一对应的。Looper 的最作用就是轮训消息队列,Hander就是发消息和处理消息,MessageQueue只负责插入、存储消息

    关于Handler有两点需要补充一下。文章开始提到的,Handler是个匿名内部类,会持有Activity的引用,假设做了一下耗时操作,Activity返回时候没有及时处理就很容易内存泄漏。避免方法有2点:

    A手动关闭这些应该回收的资源

    1.Acitivty 结束方法中,销毁后台线程,切断了链接,就直接Gc回收了,整个线程回收了,不用担心泄漏了。

    2假如有延迟发送的消息,就调用Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。

    B 将Handler声明为静态类,是的条件不成立

    static classMyHandlerextendsHandler{ WeakReference mActivityReference;

    static class MyHandler extends Handler {

        MyHandler(Activity activity) {

            mActivityReference= new WeakReference(activity);

        }

        @Override

        public void handleMessage(Message msg) {

            final Activity activity = mActivityReference.get();

            if (activity != null) {

                mImageView.setImageBitmap(mBitmap);

            }

        }

    }

    这样的话,Activity随意销毁就行了,一切交个Gc。但是这样只保证了Activity中的handler不会西楼,handler对象还是在Message中排队,故需要在onstop或者ondestory手动释掉所有消息:mHandler.removeCallbacksAndMessages(null);

    收工~~~~~

    相关文章

      网友评论

        本文标题:Handler运行原理

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