Looper与Handler解析

来源:互联网 发布:python 版本切换 编辑:程序博客网 时间:2024/04/28 20:51

写这篇文章的目的并不仅仅是为了将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的更新操作。

0 0
原创粉丝点击