Android上让View自动更新的另一种办法(非线程,异步处理).
以前在让使用View时,如果要更新界面,总是使用到线程来更新界面,但在看了SDK之后,发现APIDemo里没有使用过多线程的,使用的是另一个方法。
以前写过一个简单的小游戏,用了View,页面更新的代码如下
public void run() { while( drawing ) { try { //更新球的位置信息 update(); //通知系统更新界面,相当于调用了onDraw函数 postInvalidate(); //界面更新的频率,这里是每30ms更新一次界面 Thread.sleep(30); //Log.e(TAG, "drawing"); } catch (InterruptedException e) { e.printStackTrace(); } } }
这样做看起来很简洁,做在写程序的过程给我造成过一些麻烦,就是线程的生存周期要完全自己来负责,一个不小心的话,当这个Activity的生命周期本应该结束时,这个线程却还一直在后台运行着,相当浪费资源,Android的虚拟机不会主动杀死正在运行中的线程的。如果线程还存在着,却又想新开这个线程,又来抛出错误……还有数据同步的问题等等,这些过程细节都要考虑得周到,不然很容易导致程序被强制关闭。
然而在APIDemo中,在需要不停地重绘界面时,它是在onDraw函数的最后使用了invalidate();
APIDemo的代码请参考Graphics包内的Arcs。
主要的思路如下:
public void onDraw(Canvas canvas) { dosomething(); invalidate(); Log.v("main", "invaliate"); }
其它部分与以前的无异。这样做的好处是将绘图的线程管理工作全部交给Android来负责(Android本身会有一个线程来专门负责这绘图的),不需要再操心上面所述的问题。如果重绘过程需要有延迟的话,可以使用postInvalidateDelayed (long delayMilliseconds),相当于Thread.sleep(milliseconds)。相当好用,写View的过程少了一些不必要的烦躁。但仍有可能还会有数据的同步问题。但我想可不可以通过Handler,将所以数据的处理全部交给UI线程呢,这样不就将同步问题转得简单点了吗?得我将Handler学得深一点后再实验一下。
这个过程有点类似于尾递归,但又跟递归有实质的区别,invalidate()函数是只是负责发送请求的(在非线程外应使用postInvalidate()),执行函数时,往绘图线程的请求队列增加一个请求,当线程处理这个请求时,就会将界面重绘,这是一个异步的过程,invalidate()并没有执行onDraw,所以并不是递归,没有递归所需的栈,更不用担心爆栈什么的。我上面的代码后面添加了一个log语句,在Logcat里面可以看到有这个main的语句在不停地刷新的。
这里面也涉及到了Android的绘图机制的问题,也就是Handler机制,我现在也只是有一个很模糊的概念,等我学多点后再写出来理清下思路。总的来说,就是Android的绘图是专门由一个线程来负责的,这个线程里有一个请求队列,这个线程会不断地从队列里取出请求来绘制界面,在UI线程外企图修改界面是无效的。
看起来很简单的一句话,涉及的东西不少啊。学无止境。。。。。o(∩∩)o...哈哈