Handler小结

来源:互联网 发布:萨伏伊别墅尺寸数据图 编辑:程序博客网 时间:2024/06/15 03:17

首先有一个问题引出本篇文章:为什么更新UI线程的Handler必须在主线程中创建?
答:其实每个Handler都会关联一个消息队列,而消息队列封装在Looper中,而Looper又会关联一个线程(Looper通过ThreadLocal封装),最终每个消息队列会关联一个线程。Handler就是一个消息处理器,将消息投递给消息队列,然后再对应的线程中逐个取出消息,并且执行。因为取出消息后执行的动作在UI线程中。所以更新UI线程的Handler必须在主线程中创建。
下面从源码角度简单分析一下:
1.在Handler的构造函数中Looper的实例是通过myLooper()方法得到的:

 public Handler(Callback callback, boolean async) {       //.......        mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }      //...    }

而myLooper()中Looper是通过sThreadLocal.get()获取的:

 public static @Nullable Looper myLooper() {        return sThreadLocal.get();    }

那什么时候存储在ThreadLocal中的呢:

 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.prepare()方法中存储进去的。这也那个所有的就关联上了。
因为我们知道,在主线程中创建Handler不用prepare Looper,这事因为在主线程中系统默认创建了Looper,它在不停的独爱读分发消息,因此四大组件的调度、我们的输入事件、绘制请求才能得到处理。
ActivityThread 就是我们说的主线程,而它的 main() 方法就是当主线程的入口:

public static void main(String[] args) {    /...    Process.setArgV0("<pre-initialized>");    Looper.prepareMainLooper();    ActivityThread thread = new ActivityThread();    thread.attach(false);    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在子线程中创建Handler的时候必须手动加上prepare和loop方法,否则会抛出异常:Can’t create handler inside thread that has not called Looper.prepare()

public Handler(Callback callback, boolean async) {       //.......        mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }      //...    }

上述代码中可以看出,在Handler的构造函数中会与对Looper的判断,如果mLooper 为空,就会抛出异常。

//该写法会抛出异常!!!new Thread(){            @Override            public void run() {                handler = new Handler(new Handler.Callback() {                    @Override                    public boolean handleMessage(Message msg) {                        Log.e("TAG",msg.obj.toString());                        return false;                    }                });            }        }.start();

从上述程序中我们可以看到,当mLooper对象为空时,抛出了该异常,这是因为该线程的Looper对象还没有创建,因此sThreadLocal.get()会返回null。Handler的原理就是要与MessageQueue建立关联,并且将消息投递给MessageQueue,如果连MessageQueue都没有,那么Handler就没有存在的必要。而MessageQueue有封装在Looper中,因此创建Handler的同时必须保证Looper一定不为空。(因为sThreadLocal.get()为空即mLooper会为空,所以我们只需找到sThreadLocal.set()把mLooper穿进去,这样这里就不会为空了,就能正常运行了,而通过上述的源码发现在 Looper.prepare()的方法中sThreadLocal.set(new Looper(quitAllowed))赋值了),所以在子线程中必须手动加上Looper.prepare();

正确写法如下:

new Thread(){            @Override            public void run() {                //1.为当前线程创建Looper,并且会绑定到ThreadLocal中去                Looper.prepare();                handler = new Handler(new Handler.Callback() {                    @Override                    public boolean handleMessage(Message msg) {                        Log.e("TAG",msg.obj.toString());                        return false;                    }                });                //2.启动消息循环                Looper.loop();            }        }.start();

我们加了两处:
第一是通过Looper.prepare()来创建Looper,第二是通过Looper.loop()来启动消息循环。这样该线程就有了自己的Looper,也就有了自己的消息队列,如果只创建Looper而不启动消息循环,虽然不会抛出异常,但是通过Handler来post或者sendMessage也不会有效,因为消息虽然被加到消息队列中了,但是并没有启动消息循环,也就不会从消息队列中获取消息并且执行了。

项目地址:https://github.com/fengxiaobing/MyHandler
注。由于是练习项目,该项目的代码中也有包含Thread和Runnable的优缺点的代码,不足之处还望多多指教。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 本道祖文成武德 全福夫人要和离 她从火光中来 裴先生每天都在撒娇求和 空间逃荒:团宠小尼姑路子野 别慌,学霸老爹和我一起穿越了 薄先生突然黏她上瘾 团宠崽崽是只桃花精 我那老板柔弱不能自理 反派小媳妇的逆袭指南 我有一个大佬群 二婚后,我在豪门扮柔弱 诸天仙神热搜:主神老婆竟是魔尊 团宠小福妻又娇又软 替嫁医妃有空间 重生之农门贵夫 野性偏爱 嫁给病弱太子后我躺赢了 咸鱼娘娘一心只想翻墙 救命!破产后高冷总裁处处招惹我 冷酷将军每天都想要贴贴 何以赎光 甜腻!病娇傅少竟然暗恋我! 天价萌妻:偏执帝少心尖宠 有读心术后,战神把娘子宠上天 穿书后,我刷错了反派的好感度 摄政王怀里的团宠美人娇又软 满级千金不想掉马 女帝她就是个卖药的! 华娱激荡年代 她来看我的演唱会 快穿:娇养反派大佬做替身 不努力种田就要和相公继承皇位了 穿书末世之我是金手指 转生恶少后的魔幻日常 穿书后我成了反派男二的铁血妈粉 带着超市重返年代 新婚夜,我被冷冰冰的王爷读心了 修仙女配拿了反派剧本 影后的假面童话 克死前夫后我成了心软的神