安卓Handler详解


在安卓中由于主线程中不能进行耗时操作,我们往往需要另外开启一个线程来进行耗时操作,操作完成之后,我们通常需要使用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是创建在主线程中,也就是说主线程此时是阻塞的。

但是这并不会造成系统ANR,因为系统ANR的原因实际是因为系统通过binder发送给应用执行某种命令后,会启动一个延时的message,如果在指定的时间应用没有给出相应的回应,则会触发消息,出现ANR。

而在发送给应用时由于有了消息,则主线程不在阻塞,按时处理了任务并发送给系统确定remove掉那个延时的message,在应用的主线程继续阻塞等待下一个消息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值