Handler原理全解析

文本产生的前提是我在公司组内做的一次分享,当时列了一大堆的要点,奈何本人口拙,分享的时候该讲的内容没讲明白或者根本就没讲到,原本准备两次讲的东西,半小时就讲完了,效果也不是很好。之后因为各种原因组内分享就中断了,过了很久偶然又看到了这个提纲,就想着写成一篇文章,巩固下Handler的知识。

1.Handler用法

一般来说我们使用Handler都是在子线程中进行了耗时操作,完成之后通知主线程刷新界面,这个时候Handler是创建在主线程中的,或者是我们创建在子线程中指定了Handler的Looper是主线程的Looper,用法如下

Handler handler_main = new Handler();    // 主线程中创建Handler
Handler handler_work = new Handler(Looper.getMainLooper());   // 子线程中创建Handler

都是很简单的操作,但是这并不代表其他线程不能通知子线程做操作,只不过创建子线程的Handler稍微麻烦一点。

Looper.prepare();
Handler handler_work = new Handler();
Looper.loop();

说是麻烦,其实也就多了两行代码,需要注意的是Looper.loop一定要调用,且一定要在最后一行调用。
为什么主线程的Handler和子线程的Handler的创建起来不一样呢?其实原因是主线程的Looper已经被Loop过了,代码就在ActivityThread的main方法中
Looper.prepareMainLooper();

// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
    for (int i = args.length - 1; i >= 0; --i) {
        if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
            startSeq = Long.parseLong(
                    args[i].substring(PROC_START_SEQ_IDENT.length()));
        }
    }
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);

if (sMainThreadHandler == null) {
    sMainThreadHandler = thread.getHandler();
}

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

// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();

其实从这里我们也可以看出,一个线程是可以又多个Handler的,但是只有一个Looper。

2.ThreadLocal

那么Looper是怎么保持线程中唯一的呢?
这就要提到ThreadLocal这个类了。
我们看一下Looper.prepare这个方法

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

ThreadLocal是Looper类的静态成员,Looper共享,当我们调用prepare方法时,如果能拿到一个Looper就报错,否则就创建一个Looper存进去,从这里可以看出,当我们多次调用prepare方法是会抛出异常的。
这里我们就从一个正常的流程调用prepare分析,所以会调用setInitialValue方法,这个方法里给Thread创建了ThreadLocalMap对象,这个对象里存的对象是Looper,但是很可惜,此时的value对象是null,也就是initialValue方法返回的是一个null,而get方法在这里是直接将这个null返回了,所以会走到set方法里,而set方法的参数是直接创建了的一个Looper对象,也就是说Looper是在Looper调用prepare的时候存在Thread的ThreadLocalMap成员变量中的,这就保证了Looper在线程中的唯一。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值