子线程Looper.loop之后
Posted 爱炒饭
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了子线程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()关闭消息循环,否则会导致子线程一直运行,浪费资源。
以上是关于子线程Looper.loop之后的主要内容,如果未能解决你的问题,请参考以下文章
Looper.prepare()和Looper.loop()