子线程不能更新UI已经是一个常识了,如果两个线程同时更新UI,可能对同一个控件操作造成混乱,而更新UI涉及到整棵View树的遍历,加锁又影响效率,索性在ViewRootIlmpl类中设置一个checkThread()方法,检测当前线程和创建View的线程是否是同一个,如果不是直接Throw Exception,这就是不能在子线程更新UI的原理。但是实测发现在某些情况下可以在子线程中更新UI。
一、在onCreate中更新UI
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView)findViewById(R.id.text);
new Thread(new Runnable() {
@Override
public void run() {
textView.setText("apple");
}
}).start();
}
这个例子网上举的很多了,因为onCreate回调可能发生在ViewRootImpl创建之前,在textView.setText中的mLayout为null,所以此时是不会深入到ViewRootImpl里执行的,这个时候textView甚至没有显示出来,所以不需要调用onDraw重绘,只是单纯改变一个变量而已。
if (mLayout != null) {
checkForRelayout();
}
二、不改变大小更新UI
实测在我的小米手机上不改变当前控件大小只改变内容,可以正常更新不会闪退。这个情况网上目前只看到一篇文章提到,但是没有找到正确的原因。根据网上的传统分析,不改变大小时,checkForReLayout只会调用invalidate(改变大小会同时调用requestLayout和invalidate),invalidate的调用过程百度“invalidate源码”可以看到很多分析,简单说就是不断调用父view的invalidateChildParent,直到到达ViewRootImpl的invalidateChildParent,ViewRootImpl的invalidateChildParent,会执行checkThread()检测线程,从而闪退。然而在我的小米手机上并不会调用父view的invalidateChildParent方法。关键代码和调试截图如下:
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null && attachInfo.mHardwareAccelerated) {
// HW accelerated fast path
onDescendantInvalidated(child, child);
return;
}

在检测到硬件加速位mHardwareAccelerated为true后会不断调用父view的onDescendantInvalidated,从而避开了网上源码分析中各级view对Dirty区域的反复检测处理(我也没理解为什么要反复检测处理),直接到达ViewRootImpl的onDescendantInvalidated()方法,而该方法并没有检查线程这一步,所以在子线程中可以正常更新UI。希望这篇文章可以提示一点同样遇到此问题的人,但是还不知道这个mHardwareAccelerated从何而来,硬件加速指的又是什么。
最后
以上就是纯情身影最近收集整理的关于安卓子线程更新UI不闪退的问题的全部内容,更多相关安卓子线程更新UI不闪退内容请搜索靠谱客的其他文章。
发表评论 取消回复