Looper与Handler解析

写这篇文章的目的并不仅仅是为了将Looper和Handler,一方面是为后面其他相关的介绍做铺垫,另一方面是这个东西比较重要,自己也想认真的梳理一下,也想提醒一下大家,虽然关于这个的介绍网上已经有很多。
大家通常都会使用Handler+Thread组合,在Thread里面执行耗时的操作,然后使用Handler来执行UI的更新操作,但可能根本不知道其中的缘由,或者根本就不知在Handler的背后还有Looper的存在。
Looper和Handle的完整组合是这样的:
1、创建Looper:Looper.prepare();
2、执行消息队列的循环:Looper.loop()
3、handler向Looper的消息队列中发送消息。
首先我们来看段代码:

public class MainActivity extends Activity {
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Looper.prepare();
        Looper.loop();
        mHandler.sendEmptyMessage(0);
    }
}

这段代码完全按照上面的步骤,但是运行后,程序直接崩溃报错。当然我们一般都不是这么来用的,这里为了说明一个问题。
首先来看看错误信息:java.lang.RuntimeException: Only one Looper may be created per thread
意思是说在每个线程中只能创建一个Looper,这就证明了,我们平时在UI主线程中使用Handle的时候,其实背后已经执行过了Looper.prepare()和Looper.loop(),并且创建了Looper,不用我们再来运行了。
我们通常的用法是这样的:

public class MainActivity extends Activity {
    private Handler mHandle = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //进行界面的更新操作
            super.handleMessage(msg);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        new Thread(new Runnable() {
            @Override
            public void run() {
                //执行耗时操作
                //执行完了通知UI线程进程进行界面更新
                mHandle.sendEmptyMessage(0);
            }
        }).start();

    }
}

当然上面的代码存在着内存泄漏的问题,这里我们先不做考虑,把心思放在我们今天要讲的重点上,关于为什么出现这个问题后面再做讨论。这里就直接在我们定义的线程和UI线程中使用Handle进行通信,下面我们要进行两步讲解:

1、在UI线程中,Looper.prepare()和Looper.loop()在哪里执行的?

在Activity启动过程中会执行系统源码frameworks/base/core/java/android/app/ActivityThread.java文件。
我们来看看里面的main()函数:

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

这下你应该看到了两个比较熟悉的面孔了吧!

2、怎样使用Looper和Handler实现线程间的通信?

首先让我们来看看Looper这个类主要包含哪些东西:
这里写图片描述

里面有一个ThreadLocal对象,有一个MessageQueue对象。
ThreadLocal主要用来保存数据,它是主要跟线程绑定,即存放对应线程中对应的数据,具体的原理这里就不展开了。
再来看源码,首先看Looper.prepare():

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

看到if语句里面抛出的异常,是不是感觉比较熟悉,我们前面的那个异常就是这里抛出的,在prepare里面会创建一个Looper对象,把它放入ThreadLocal里面,但是在这个线程里面,Looper对象只能创建一次,所以前面会首先取出当前线程中的Looper进行判断,看是否已经创建,如果已经存在就会报错,这也是我们前面报错的原因,也就是说Looper对象只能有一份。
再看看Looper.loop()函数:

public static void loop() {
        final Looper me = myLooper();
        //从返回的异常可以看出,调用这个函数前,必须先调用Looper.prepare()函数。
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //在创建Looper对象的构造函数中,会创建消息队列,所以这里就可以直接使用
        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);
            }
            //target里面存放的是Handle对象,使用handle进行消息分发,最终就会回调handleMessage方法
            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();
        }
    }
现在我们来好好的分析一下我通常一般使用Handler的内部流程:
public class MainActivity extends Activity {
    private Handler mHandle = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //进行界面的更细操作
            super.handleMessage(msg);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        new Thread(new Runnable() {
            @Override
            public void run() {
                //执行耗时操作
                //执行完了通知UI线程进程进行界面更新
                mHandle.sendEmptyMessage(0);
            }
        }).start();

    }
}

上面已经提过,在UI线程中已经运行过了Looper.prepare()和Looper.loop()。所以在UI线程中已经有一个Looper对象,并且在Looper对象中有一个消息队列。
在我们自定义线程中来向UI线程发送消息是怎么实现的呢?我接着看Handle里面的源码,一步一步进行跟踪,我们先看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());
            }
        }
        //这里获取到UI线程中创建的那个Looper对象
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }

        //获取Looper里面的消息队列
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
接着就是发送消息的流程:
public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        //创建一个消息,把这个消息发送出去
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        //把Looper里的消息队列赋值给queue
        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);
    }

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //这里会把handler对象赋值给Message的taget对象,这样就可以在消息队列中回调Handle里面的handleMessage方法
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //最终往Looper那个对象的消息队列中把消息放进去
        return queue.enqueueMessage(msg, uptimeMillis);
    }

消息放进去了之后,我们再来看看上面的Looper.loop()函数,在里面有msg.target.dispatchMessage(msg)这条语句,获取到消息后,因为msg的target里面存放的是就是这个Handle对象,这样就可以得到这个Handler对象,然后利用这个Handler进行消息的分发。
我们接着跟踪源码:

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

可以看到在这里最终回调到了handleMessage这个方法。这样我们就在UI线程中通过Looper的消息队列获取到这个消息,并且回调了Handler里的handleMessage方法,就可以进行UI的更新操作。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值