Handler运行原理

出现背景:

为了方便线程之间的通讯。比如:主线程处理耗时操作就会引起卡顿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运行原理)