原文地址

转载请注明原文出处

===============================================================================

总是感觉android中UI更新很让人纠结!自己小结一下,算是抛砖引玉。读这篇文章之前,假设你已经明白线程、handler的使用。


1. 在onCreate()方法中开启线程更新UI

view plain print ?
  1. publicclassMasterActivityextendsActivity{
  2. TextViewtv=null;
  3. Buttonbtn=null;
  4. @Override
  5. publicvoidonCreate(BundlesavedInstanceState){
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.main);
  8. System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
  9. tv=(TextView)findViewById(R.id.text);
  10. btn=(Button)findViewById(R.id.btn);
  11. /*onCreate中开启新线程,更新UI。没有报错或者异常信息!*/
  12. Threadthread=newThread(newRunnable(){
  13. @Override
  14. publicvoidrun(){
  15. System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
  16. tv.setText("updateUIissuccess!");
  17. btn.setText("updateUIissuccess!");
  18. }});
  19. thread.start();
  20. }

随便折腾,不会报错或者异常!以为开启的线程和UI线程(主线程)是同一个线程,但是很不幸,他们的线程id根本是风牛马不相及!

不知道为什么在这里开启子线程更新UI就没有问题!真的想不明白????


2. 在activity如onResume、onStart、反正是以on开头的回调方法

view plain print ?
  1. @Override
  2. protectedvoidonRestart(){
  3. super.onRestart();
  4. /*onRestart中开启新线程,更新UI*/
  5. Threadthread=newThread(newRunnable(){
  6. @Override
  7. publicvoidrun(){
  8. System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
  9. tv.setText("updateUIissuccess!");
  10. btn.setText("updateUIissuccess!");
  11. }});
  12. thread.start();
  13. }

不好意思,按下返回按钮在启动程序,或者按Home健再启动程序,就这么折腾几下,就会包异常!信息如下:

android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

意思是:只有主线程才可以更新UI。

解决办法:加上postInvalidate()方法。

view plain print ?
  1. @Override
  2. protectedvoidonRestart(){
  3. super.onRestart();
  4. /*onRestart中开启新线程,更新UI*/
  5. Threadthread=newThread(newRunnable(){
  6. @Override
  7. publicvoidrun(){
  8. System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
  9. tv.postInvalidate();
  10. btn.postInvalidate();
  11. tv.setText("updateUIissuccess!");
  12. btn.setText("updateUIissuccess!");
  13. }});
  14. thread.start();
  15. }

postInvalidate()方法,源码:

view plain print ?
  1. publicvoidpostInvalidate(){
  2. postInvalidateDelayed(0);
  3. }
  4. publicvoidpostInvalidateDelayed(longdelayMilliseconds){
  5. //WetryonlywiththeAttachInfobecausethere'snopointininvalidating
  6. //ifwearenotattachedtoourwindow
  7. if(mAttachInfo!=null){
  8. Messagemsg=Message.obtain();
  9. msg.what=AttachInfo.INVALIDATE_MSG;
  10. msg.obj=this;
  11. mAttachInfo.mHandler.sendMessageDelayed(msg,delayMilliseconds);
  12. }
  13. }

其实,是调用了Handler的处理消息的机制!该方法可以在子线程中直接用来更新UI。还有一个方法invalidate(),稍候再说!


3. 在Button的事件中开启线程,更新UI

view plain print ?
  1. publicclassMasterActivityextendsActivity{
  2. TextViewtv=null;
  3. Buttonbtn=null;
  4. @Override
  5. publicvoidonCreate(BundlesavedInstanceState){
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.main);
  8. System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
  9. tv=(TextView)findViewById(R.id.text);
  10. btn=(Button)findViewById(R.id.btn);
  11. btn.setOnClickListener(newOnClickListener(){
  12. @Override
  13. publicvoidonClick(Viewv){
  14. Threadthread=newThread(newRunnable(){
  15. @Override
  16. publicvoidrun(){
  17. System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
  18. tv.setText("updateUIissuccess!");
  19. btn.setText("updateUIissuccess!");
  20. }});
  21. thread.start();
  22. }
  23. });
  24. }

Sorry,报错!即使你加上postInvalidate()方法,也会报这个错误。

android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Android: 如何利用Handler、Thread更新视图_第1张图片


4. 使用Handler结合多线程更新UI
a. 开启一个线程,在run方法中通知Handler
b. Handler中使用handleMessage方法更新UI。
view plain print ?
  1. publicclassMasterActivityextendsActivity{
  2. TextViewtv=null;
  3. Buttonbtn=null;
  4. HandlermHandler=newHandler(){
  5. @Override
  6. publicvoidhandleMessage(Messagemsg){
  7. if(msg.what==1){
  8. tv.setText("updateUIissuccess!");
  9. btn.setText("updateUIissuccess!");
  10. }
  11. super.handleMessage(msg);
  12. }
  13. };
  14. @Override
  15. publicvoidonCreate(BundlesavedInstanceState){
  16. super.onCreate(savedInstanceState);
  17. setContentView(R.layout.main);
  18. System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
  19. tv=(TextView)findViewById(R.id.text);
  20. btn=(Button)findViewById(R.id.btn);
  21. btn.setOnClickListener(newOnClickListener(){
  22. @Override
  23. publicvoidonClick(Viewv){
  24. Threadthread=newThread(newRunnable(){
  25. @Override
  26. publicvoidrun(){
  27. System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId());
  28. Messagemsg=mHandler.obtainMessage();
  29. msg.what=1;
  30. msg.sendToTarget();
  31. }});
  32. thread.start();
  33. }
  34. });
  35. }
5. Handler和invalidate 方法结合多线程更新UI
方法invalidate主要用在主线程中(即UI线程中), 不可以用于子线程。如果在子线程中需要使用postInvalidate方法。 sdk的api有说明: view plain print ?
  1. publicvoidinvalidate()
  2. Since:APILevel1
  3. Invalidatethewholeview.Iftheviewisvisible,onDraw(Canvas)willbecalledatsomepointinthefuture.ThismustbecalledfromaUIthread.Tocallfromanon-UIthread,callpostInvalidate().
看看该方法源码: view plain print ?
  1. publicvoidinvalidate(){
  2. if(ViewDebug.TRACE_HIERARCHY){
  3. ViewDebug.trace(this,ViewDebug.HierarchyTraceType.INVALIDATE);
  4. }
  5. if((mPrivateFlags&(DRAWN|HAS_BOUNDS))==(DRAWN|HAS_BOUNDS)){
  6. mPrivateFlags&=~DRAWN&~DRAWING_CACHE_VALID;
  7. finalViewParentp=mParent;
  8. finalAttachInfoai=mAttachInfo;
  9. if(p!=null&&ai!=null){
  10. finalRectr=ai.mTmpInvalRect;
  11. r.set(0,0,mRight-mLeft,mBottom-mTop);
  12. //Don'tcallinvalidate--wedon'twanttointernallyscroll
  13. //ourownbounds
  14. p.invalidateChild(this,r);
  15. }
  16. }
  17. }
invalidate 方法如果你直接在主线程中调用,是看不到任何更新的。需要与Handler结合! 感谢这位“雷锋”,一个不错的例子: http://disanji.net/2010/12/12/android-invalidate-ondraw/ 只是被我修改了一点,加入times,看看onDraw到底运行多少次。

Android在onDraw事件处理绘图,

invalidate()函数可以再一次触发onDraw事件,然后再一次进行绘图动作。

view plain print ?
  1. publicclassMasterActivityextendsActivity{
  2. staticinttimes=1;
  3. /**Calledwhentheactivityisfirstcreated.*/
  4. @Override
  5. publicvoidonCreate(BundlesavedInstanceState){
  6. super.onCreate(savedInstanceState);
  7. setContentView(newView(null){
  8. PaintvPaint=newPaint();//绘制样式物件
  9. privateinti=0;//弧形角度
  10. @Override
  11. protectedvoidonDraw(Canvascanvas){
  12. super.onDraw(canvas);
  13. System.out.println("thisrun"+(times++)+"times!");
  14. //设定绘图样式
  15. vPaint.setColor(0xff00ffff);//画笔颜色
  16. vPaint.setAntiAlias(true);//反锯齿
  17. vPaint.setStyle(Paint.Style.STROKE);
  18. //绘制一个弧形
  19. canvas.drawArc(newRectF(60,120,260,320),0,i,true,vPaint);
  20. //弧形角度
  21. if((i+=10)>360)
  22. i=0;
  23. //重绘,再一次执行onDraw程序
  24. invalidate();
  25. }
  26. });
  27. }
  28. }

经过测试,发现times一直在++,说明onDraw被多次调用,并且一致在画图!

sdk的api有时候让人很郁闷,无语.....关于invalidate的使用,还待探索。革命尚未成功,同志仍需努力!


更多相关文章

  1. android设置屏幕禁止休眠的方法
  2. Android中使用Streams的两种方法
  3. 我的Android进阶之旅------>Ubuntu下不能识别Android设备的解决
  4. 动态修改Android参数信息的方法绕过改机检测
  5. android 横竖屏限制的配置方法
  6. Android file.createNewFile方法问题总结

随机推荐

  1. 一定要看的 Android(安卓)资源目录的相关
  2. Android(安卓)系统属性
  3. 使用VS2015调试Android的C++动态链接库
  4. Intent及其属性详解
  5. android api code学习之ActivityManager
  6. Android(安卓)开发经验汇总
  7. Android之EventBus1.0 和EventBus3.0的使
  8. android 命令
  9. iOS开发 OC与java相对应的3DES加解密
  10. Google Android如何分析和研究Log文件 ,如