接着我们上一篇文章提出来的问题,我特意去源码中寻找了答案,我们现在知道只要我们主线程暂停的时候没有请求就不会抛出无响应(ANR)异常,那你可能这样想,比如下面这几行代码
for (int i=0;i<10;i++){
try {
//这里是更新图片高度与宽度
UploadImage(500 + i*10,500 + i*10);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
现在我来第一种解法,我们在主线程中直接执行这个代码,看上去应该没什么问题,我们更新了图片高度以后,然后主线程停下来一秒钟,然后停的这一秒我们什么请求也没有,然后休眠完毕,线程启动我们又更新一次图片高度,没毛病啊。嗯,确实没毛病,这样也不会抛出ANR异常,但是但是但是这样会有一个问题界面ui只会在循环的最后一次刷新。
为什么会出现这个问题,我们首先来了解一下我们调用刷新ui的方法
m4I1.setLayoutParams(params);
这个方法会调用view的 requestLayout() 方法,然后根据责任是链条一直到 ViewRootImpl 上调用requestLayout() 最后走到了 scheduleTraversals()这个方法
void scheduleTraversals() {
if (!mTraversalScheduled) {
.....
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
.....
}
}
看上面我留下来的一行代码,这是个异步刷新看到没,异步不是同步!!!!! mTraversalRunnable是个Runner 这里会去调用 doTraversal方法
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
最后doTraversal方法中调用了 performTraversals() ,在performTraversals方法中会去调用onLayout onMeasure onDraw 进行重绘。
但是这里是个异步也就是你都没来得及刷新完毕线程就暂停了。所以就刷新不了等下一次,如此往复到最后一次线程不暂停了,它终于可以完完整整的刷新完毕一次。
这就是为什么只有最后一次才会更新ui的原因了。所以建议不要这样使用,如果你有需求建议开一个子线程,或者你可以使用计时器 CountDownTimer 来实现,这里我使用了 CountDownTimer 来替代计时。