子线程Looper.loop之后

android规定不允许子线程更新UI,在ViewRootImpl 类中很多操作UI的方法都会调用checkThread()方法检查线程,如果当前线程与创建ViewRootImpl (mThread )的线程不一致就会报“Only the original thread that created a view hierarchy can touch its views”

//ViewRootImpl.java
void checkThread() {
    if (mThread != Thread.currentThread()) {//检查当前方法调用线程与mThread是否一致
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}
……
public ViewRootImpl(Context context, Display display) {
    ……
    mThread = Thread.currentThread();//mThread是构造ViewRootImpl时的线程
    ……
}

ViewRootImpl是在什么时候创建的呢?在 从Activity的setContentView方法说起一文中有提到ViewRootImpl是在
WindowManagerGlobal.java 中addView方法中创建的,所以这个线程还是操作UI的线程。

//WindowManagerGlobal.java  
public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
        ……
        root = new ViewRootImpl(view.getContext(), display);
        ……
}

通常情况下可以通过主线程的handler来发送和处理消息,但是在某些情况下子线程拿不到handler或者不方便通过其他方式给主线程发送消息,as we all konw,子线程弹toast会导致应用挂掉。Handler,Looper,Message的理解与困惑一文最后总结道通过Looper.prepare()和 Looper.loop()就可以来创建和循环处理消息,消息的创建和处理在同一个线程就不怕线程不一致的问题了, 此时有些同学可能会有类似下面的骚操作

thread = new Thread(new Runnable() {
    @Override
    public void run() {
        LogUtil.d("run start");
        Looper.prepare();
        Toast.makeText(MainActivity.this,"hahha",Toast.LENGTH_SHORT).show();
        LogUtil.d("run 2");
        Looper.loop();
        LogUtil.d("run end");
    }
});
thread.start();

第5行通过Looper.prepare()方法可以为当前线程创建一个Looper对象,并放置到当前线程的ThreadLocal中,方便后续获取该Looper对象,ThreadLocal相关知识可以参考 ThreadLocal和InheritableThreadLocal一文。
第8行的Looper.loop()方法从当前线程的ThreadLocal中获取Looper对象,然后开启消息死循环。还记得android app为什么没有自动退出吗?其实就是在ActivityThread.java的main方法的最后通过Looper.loop()开启了消息死循环,这个死循环有两个方面作用:1、等待消息,有消息就处理,没有消息就休眠;2、防止程序执行结束就死掉。
上面的demo运行后log打印如下:
在这里插入图片描述
Looper.loop()下面的log没有打印,并且通过monitor.bat查看18531线程一直在运行。这样的话,该线程可能一直不会消亡(除非app进程死掉)。在这里插入图片描述
修改下代码,如果去掉Looper.prepare()、Toast.makeText、Looper.loop三行代码,程序运行完子线程就会结束。
log打印如下:
在这里插入图片描述
monitor.bat查看19324线程已经消亡了
在这里插入图片描述
所以子线程通过Looper.prepare和Looper.loop方式操作UI的方式还是要慎用,不用时要通过Looper.myLooper().quitSafely()关闭消息循环,否则会导致子线程一直运行,浪费资源。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值