安卓Handler详解

来源:互联网 发布:阿里云怎么赚钱 编辑:程序博客网 时间:2024/06/08 00:28


在安卓中由于主线程中不能进行耗时操作,我们往往需要另外开启一个线程来进行耗时操作,操作完成之后,我们通常需要使用Handler来将结果展现在界面中,下面我来说明下handler的使用。

Handler的创建

public Handler mHandler = new Handler();

Handler的创建只要new 一个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();if (mLooper == null) {    throw new RuntimeException(        "Can't create handler inside thread that has not called Looper.prepare()");}mQueue = mLooper.mQueue;
mQueue是MessageQueue类的一个对象,是一个消息队列,主要的作用就是储存由一个线程发送到主线程的消息,而Looper则是用来对MessageQueue来进行管理。

如果在该线程中没有Looper类,则会报错

if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }
在Activity中,由系统自动给我们创建Looper。

Handler其实也可以创建在非主线程中,前提是在非主线程中,我们手动的创建Looper,即调用Looper.prepare()方法。

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));    }
那么 怎么确保在一个线程中只有一个Looper呢,这要通过sThreadLocal来实现

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public void set(T value) {        Thread currentThread = Thread.currentThread();        Values values = values(currentThread);        if (values == null) {            values = initializeValues(currentThread);        }        values.put(this, value);    }public T get() {        // Optimized for the fast path.        Thread currentThread = Thread.currentThread();        Values values = values(currentThread);        if (values != null) {            Object[] table = values.table;            int index = hash & values.mask;            if (this.reference == table[index]) {                return (T) table[index + 1];            }        } else {            values = initializeValues(currentThread);        }        return (T) values.getAfterMiss(this);    }
上面就是ThreadLocal的get和set方法,简单点说就是将Looper与创建Looper的线程采用一一对应的关系保存起来,以此来确保在一个线程中只有一个Looper。

就是说如果在一个线程中创建了多个Looper,则会报错,并提示Only one Looper may be created per thread,如果在该线程中没有创建过Looper,则新建一个Looper

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

由他的构造方法可以看到,创建Looper的同时,系统会创建MessageQueue对象,这个对象也就是上面提到的Handler创建时的那个MessageQueue对象,所以在一个线程中只有一个Looper,也即只有一个MessageQueue对象,但是需要注意的是可以有多个Handler对象,那怎么确保由正确的Handler对象处理由其发送的Message呢,我们接着看源码。

在子线程中我们一般都是调用Handler的sendMessage方法发送消息,其实Handler的发送消息的方法,包括sendMessage,sendEmptyMessage,sendEmptyMessageDelayed等经过处理,最终都会调用下面这个方法

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);    }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }
可以看到,在enqueueMessage方法中msg.target的值为this,也就是这个Handler对象本身,就是说只要有这个message,就可以通过target这个参数知道是有谁发送的,所以就解决了上面关于如果有多个Handler怎么正确处理消息的问题。
现在完成了消息的发送。

下面我们看下怎么处理消息。

Looper的创建一般包括两部分,一个是Looper.prepare(),我们上面已经说过了,还有一个就是Looper.loop(),这个loop就是取消息,函数中会有一个死循环,不停地从MessageQueue中获取消息,即获取我们发送的Message对象,再通过Message中的target也就是发送消息的Handler对象的handMessage方法实现回调,完成从一个线程发送消息到另一个线程。

需要注意的是在loop方法中他会有一个类似锁的机制,即当MessageQueue中没有消息的时候,程序会阻塞在消息队列中,由于Looper是创建在主线程中,也就是说主线程此时是阻塞的。

我查了一些资料我觉得其中比较靠谱的一种说法是这样的:系统它本身也是通过Handler这种方法实现刷新UI的,而且系统刷新的时间是很快的,每16ms都要进行一次刷新,所以Looper顶多也就阻塞十几毫秒左右,并不会造成系统的ANR。这只是我的认识,欢迎指正

0 0
原创粉丝点击