Android Handler机制理解

Handler机制理解:
Handler Thread Looper MessageQueue
代码中常用到在非UI线程中使用handler.sendMessage(int what)来发送一个消息到UI线程中更
新界面,下面解析一下handler机制,详解其消息是怎么从发送到处理的过程。

1.生成一个Handle对象。
我们通常在Acticity中生成一个Handler对象是直接调用new Handler()来创建Handler的,好
吧,看看此构造函数干了些神马。

     public Handler() {
        this(null, false);
    }
     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 = Looper.myLooper();

        mQueue = mLooper.mQueue;
Looper的结构后续在详说,我们这里先了解Handler中初始化了一个Looper,并且拿到了Looper
中的一个MessageQueue成员。

2.发送消息,handler.sendMessage();
我们可以发送一个Message消息对象或者一个Runnable对象,我们这里只说说Message,Runnable
类似。调用handler.sendMessage(),实际上是到最后调用到的是如下方法:

    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            msg.target = this;
            sent = queue.enqueueMessage(msg, uptimeMillis);
        }else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
    }

明显,这里是直接用上面提到的Looper中的MessageQueue对象直接把Message对象入队,值得
注意的一点是,在入队之前,Message对象把当前的Handler对象给收了:msg.target = this,
也就是说,消息队列中的每个消息对象都拿着一个Handler引用,那这个引用干什么用呢?看下
面。

3.消息处理,handler.handleMessage();
一般handleMessage(Message msg)这个方法是我们重写来处理消息用的,那这个方法是被谁调
用的呢?
首先我们已经知道了我们sendMessage()的时候已经把一个Message对象入队到了Looper中
的MessageQueue的对象中了,可以想象到我们需要一个线程去从队列中把一个一个消息拿出来
交给Handler去处理,到了这里,我们就不能不开始解释Looper了。

4.Looper类说明
我们先看看Looper的文档:
Looper类可以用作一个线程中的消息循环器,默认的线程是没有消息循环的,必须在线程
中调用Looper.prepare()来创建一个循环器,用此循环体来获取所要处理的消息,直到整个循
环器停止工作为止。

说明文档中的典型例子:

   class LooperThread extends Thread {
      public Handler mHandler;


      public void run() {
          Looper.prepare();


          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }
    意思就是说Looper充当一个消息队列循环器,在线程启动时先调用Looper.prepare(),在prepare()
方法中,会调用Looper的私有构造函数创建一个Looper:
    public static void prepare() {
        prepare(true);
    }


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


    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mRun = true;
        mThread = Thread.currentThread();
    }

    比较关键的是在构造函数中new了一个对象:mQueue。
    没错,在第1点中说的Handler的构造方法中就出现过此对象:mQueue = mLooper.mQueue。现
在清楚了在Looper对象创建的时候,就创建了一个MessageQueue消息对列的对象,用来传递给后
面关联的Handler对象,Handler对象在sendMessage()方法中拿着Looper中的mQueue把消息一个一
个往里面塞。
    那塞完Message对象,这些对象怎么循环处理的呢?我们在上面的官网的例子中还看到了一个

函数:Looper.loop(),没错了,就是这个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
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

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

    这个有点长,不过思路很清晰:
    1)从当前Looper中拿出消息队列:final MessageQueue queue = me.mQueue;
    2)做一个死循环遍历取出消息队列中的消息对象:Message msg = queue.next();
    3)用msg.target去处理每一条消息: msg.target.dispatchMessage(msg)。还记得我们上面
第2点中提到的Handler中的Message对象在入队前干了什么事情么?对了,Message对象把
当前的Handler对象给收了:msg.target = this。所以说,现在这里处理消息的时候用的
msg.target.dipatchMessage(msg)中的target就是当年handler.sendMessage(msg)的时候
的那个handler对象,所以消息处理的时候,哪个handler对象发的消息,最后还回到这个
handler对象中处理,再次可以想象到如果同一个线程中new了多个Handler对象,他们都
是只关联上了一个Looper而已,也就是说多个Handler对象发送的消息都在一个消息队列
中排队,并且这些消息最终会回到各自的Handler对象中处理。
    4)在handler.depathMessage(msg)中又干了什么事情呢?源码:

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

是不是看到了我们熟悉的函数:handleMessage(msg),这不就是我们第3点提到的我们自己重
写来自己处理消息的方法吗?好了,现在从发送消息到最后处理消息都说完了。
关键方法执行顺序:

Looper.prepare --> new Handler --> handler.sendMessage -->message.target = this --> mQueue.enqueueMessage --> Looper.loop 

-->message.target.dipatchMessage --> handler.handleMessage

 到了这里,已经很明显可以看出,任意线程中,如果需要用到Looper这个消息循环器的话就
一定需要先调用Looper.prepare()来创建Looper,并且调用Looper.loop(),来循环消息。但是话
说回来我们在Acticity中创建Handler时并没有看到什么Looper的身影啊,那他是在什么时候就
开始prepare了呢?这个其实系统在创建一个应用进程的时候已经把这个事情给帮我们做了,是在
:ActivityThread类中。Android启动一个应用进程的时候,入口是ActivityThread中的main方法。
对了,你没看错,就是我们java类的入口方法main函数,源码:

    public static void main(String[] args) {
        SamplingProfilerIntegration.start();


        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);


        Process.setArgV0("<pre-initialized>");


        Looper.prepareMainLooper();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }


        ActivityThread thread = new ActivityThread();
        thread.attach(false);


        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }


        Looper.loop();


        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
这里就是我们常说的主线程或者UI线程了,是不是看到了Looper.prepareMainLooper()和

Looper.loop()的调用?Looper.prepareMainLooper()源码就自己去Looper中看吧!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值