今天来谈谈利用View类的post() 方法来更新UI。
或许有人会问在Android系统设计的时候为什么不引入多线程直接操作UI呢?
为了回答这个问题,我们首先可以思考引入多线程操作UI会带来哪些问题?
1 线程同步问题
多个线程同时操作一个对象,为保证互斥资源数据的安全性就要进行线程同步,由于线程执行时间片和执行顺序的不确定性,由线程同步引起的编程复杂性会有很大提高。
2 效率的问题
代码中进行线程同步会使得没有持有锁的线程等待,使得并行处理在访问互斥资源时变成串行,拖慢程序执行速度。
基于此种考虑,Android设计了一套机制来保证线程更新UI的安全和高效。大体设计思路是:
1.主线程持有一个消息队列MessageQueue用来存放Message(消息),Message中存放的是需要执行的代码逻辑,例如更新UI,根据Activity状态回调生命周期相应方法等操作。
2.创建一个Lopper对象来不断的(可认为是死循环)从MessageQueue中取出Message(消息)进行处理。
3.提供了Handler类来操作MessageQueue,利用Handler的sendMessage方法来向UI线程发送消息(存放操作)。
队列是FIFO(先进先出)的数据结构,这样UI线程不断的从消息队列中取出Message进行处理,处理完毕取出下一个Message,直到MessageQueue中的Message全部处理完毕,继续不断轮询等待新消息进行处理。
为了方便编码操作UI,Android为View类提供了post()方法来传入一个实现了Runnable接口的对象,注意调用Runnable的并不一定要新建线程,而是标示出要调用该对象run()方法内的代码逻辑。所以在其中不能有耗时操作。查看View中post()方法的源码点我点我
View.post():
public boolean post(Runnable action) {
Handler handler;
AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
handler = attachInfo.mHandler;
} else {
// Assume that post will succeed later
ViewRootImpl.getRunQueue().post(action);
return true;
}
return handler.post(action);
}
Handler.post():
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
Handler.sendMessageDelayed()
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
......
可以看到 post()方法中的参数签名叫做 action,也表示要调用的动作的含义。在此方法中最终还是调用Handler来向消息队列中添加Message,通过Looper的不断循环使得代码逻辑最终得到处理。
需要提醒一点,Android的View绘制是在主线程中,所以在绘制View的时候如果有复杂耗时的操作,就会导致用户的触摸事件无法快速传递到MessageQueue中,应用无法及时响应用户的操作而导致用户体验差。遇到此类问题的时候可以使用SurfaceView来代替View的绘制。
好了,本文到此为止,咱们下篇再见!