为啥要在主线程操作UI

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为啥要在主线程操作UI相关的知识,希望对你有一定的参考价值。

参考技术A UIKit不是线程安全的
试想下面这几种情况:

1.两个线程同时设置同一个背景图片,那么很有可能因为当前图片被释放了两次而导致应用崩溃。
2.两个线程同时设置同一个UIView的背景颜色,那么很有可能渲染显示的是颜色A,而此时在UIView逻辑树上的背景颜色属性为B。
3.两个线程同时操作view的树形结构:在线程A中for循环遍历并操作当前View的所有subView,然后此时线程B中将某个subView直接删除,这就 导致了错乱还可能导致应用崩溃。

UIKit并不是一个 线程安全 的类,UI操作涉及到渲染访问各种View对象的属性,如果异步操作下会存在读写问题,而为其加锁则会耗费大量资源并拖慢运行速度。另一方面因为整个程序的起点UIApplication是在主线程进行初始化,所有的用户事件都是在主线程上进行传递(如点击、拖动),所以view只能在主线程上才能对事件进行响应。而在渲染方面由于图像的渲染需要以60帧的刷新率在屏幕上 同时 更新,在非主线程异步化的情况下无法确定这个处理过程能够实现同步更新。

ios 为什么必须在主线程中操作UI
iOS拾遗——为什么必须在主线程操作UI

为何invalidate()不可以直接在UI线程中调用

1、android ui操作为什么一定要在主线程中执行?

答:Android UI操作是单线程模型,关于UI更新的相关API(包括invalidate())都是按照单线程设计的,对于多线程运行时不安全的,即在非主线程调invalidate()刷新界面出现异常。所以android禁止在非主线程更新UI。

2、为什么说invalidate()是线程不安全的?
答:在非UI线程中调用invalidate会导致线程不安全,也就是说可能在非UI线程中刷新界面的时候,UI线程(或者其他非UI线程)也在刷新界面,这样就导致多个界面刷新的操作不能同步,导致线程不安全。


3、为何invalidate()不可以直接在UI线程中调用?
答:Android主线程的执行,是通过Looper循环调度消息的机制运行的;主线程一直在执行Looper循环中,更新UI需要通过Handler往消息队列发送消息,Looper调度到以后便可以执行invalidate()。


android中实现view的更新有两组方法,一组是invalidate,另一组是postInvalidate,其中前者是在UI线程自身中使用,而后者在非UI线程中使用。 
Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用,因为他违背了Android UI操作的单线程模型。 

invalidate方式刷新界面需要借助handler发送消息给UI线程,排队轮到时,收到刷新指令,才调用invalidate()开始刷新

postInvalidate方式底层的实现,是和上述invalidate方式一样的,借助于handler发送消息。源码参考https://blog.csdn.net/ziwang_/article/details/65690751

// 在onCreate()中开启线程
 
new Thread(new GameThread()).start();、
 
// 实例化一个handler
 
Handler myHandler = new Handler() {
// 接收到消息后处理
public void handleMessage(Message msg) {
switch (msg.what) {
case Activity01.REFRESH:
mGameView.invalidate(); // 刷新界面
break;
}
 
super.handleMessage(msg);
}
};
 
class GameThread implements Runnable {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
Message message = new Message();
message.what = Activity01.REFRESH;
// 发送消息
Activity01.this.myHandler.sendMessage(message);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
class GameThread implements Runnable {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
 
// 使用postInvalidate可以直接在线程中更新界面
mGameView.postInvalidate();
}
}
}
 

 








以上是关于为啥要在主线程操作UI的主要内容,如果未能解决你的问题,请参考以下文章

UI更新为什么一定要在UI线程里?幕后真相究竟如何?

UI更新为什么一定要在UI线程里?幕后真相究竟如何?

如何在android一条单独线程,更新ui ?

为啥在主线程上调用东西是程序员的责任?

为何invalidate()不可以直接在UI线程中调用

Android——消息分发机制