什么是UI线程
Android的核心进程zygote进程fork出我们的app,app启动的最终会走入到ActivityThread中的main方法,在main方法中会调用Looper。其中ActivityThread所在的线程被称为UI线程,也就是我们常说的主线程 (Main thread)。 关于Main thread这个称呼其实可以查看ActivityThread中main方法的源码:
public static void main(String[] args) {
... ...
//注释1
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
注释1:只看最后两行代码,在Loop.loop();调用完直接抛出异常,这里异常提到了当前线程称之为Main thread.
UI线程的工作机制
需要理解UI线程的工作机制,就需要了解Android的消息机制。简单的图片能描述这个概念,如下图
UI线程为什么不能更新
构建一个子线程更新UI的例子
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
showTv = findViewById(R.id.show_tv);
new Thread(){
@Override
public void run() {
super.run();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
showTv.setText("子线程显示UI");
}
}.start();
}
运行之后会抛出异常
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7957)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1274)
at android.view.View.requestLayout(View.java:23120)
at android.view.View.requestLayout(View.java:23120)
at android.view.View.requestLayout(View.java:23120)
at android.view.View.requestLayout(View.java:23120)
at android.view.View.requestLayout(View.java:23120)
at android.view.View.requestLayout(View.java:23120)
at androidx.constraintlayout.widget.ConstraintLayout.requestLayout(ConstraintLayout.java:3172)
at android.view.View.requestLayout(View.java:23120)
at android.widget.TextView.checkForRelayout(TextView.java:8914)
at android.widget.TextView.setText(TextView.java:5736)
at android.widget.TextView.setText(TextView.java:5577)
at android.widget.TextView.setText(TextView.java:5534)
at com.example.myapplication.MainActivity$1.run(MainActivity.java:29)
这里在子线程中添加了Thread.sleep做一些耗时操作
从抛出的异常信息可以定位到相应的源码ViewRootImpl.java
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
//注释2
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
注释2:从代码中可以看出抛出异常的一部分,会被requestLayout()调用,如果了解view的绘制过程可以知道requestLayout是被调用的方法之一。
在实例代码中添加了Thread.sleep()代码来执行耗时,如果没有这一耗时操作,执行setText更新UI的操作会被正常执行,个人看法:1. 这里需要再深入探究一下mHandlingLayoutInLayoutRequest这个boolean值变量的被改变的时机。2.在mThread初始赋值为Thread,执行到相关代码Thread.currentThread()没有变?多一点探究思路可以更好的阅读源码,有的放矢。
Android 是否提供了非UI线程更新UI的一些控件
这个问题,我们需要另外探究如SufaceView相关类的实现方式,就可以得到我们想要的答案。
非UI线程想要更新UI怎么办
Handler发送message的方式
postInvalidate()
这两种方式我们也可以间接的更新UI
思考
一个简单的问题,入手点其实还是有很多内容,这么多内容需要自己不断看源码发现总结来获取。
最后
如果你看到了这里,觉得文章写得不错就给个赞呗!欢迎大家评论讨论!如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足,定期免费分享技术干货。喜欢的小伙伴可以关注一下哦。谢谢!