原文地址

转载请注明原文出处

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

总是感觉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.


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. smack 源码分析- PacketWriter (android上实现长连接)
  2. Android(安卓)layer type与WebView白屏
  3. android设置屏幕禁止休眠的方法
  4. Android四大基本组件介绍与生命周期
  5. Android(安卓)相机实例
  6. android 带箭头的textview文字伸缩效果
  7. 浅析Android中Handler机制
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. 修改Android默认背光值
  2. Android game engine list
  3. Android异步加载图片,并缓存到SD卡
  4. android中获取文字的宽度
  5. Android平台上PMEM的使用及Platform设备
  6. Android Bitmap 相关
  7. android SDK 环境变量配置+ADT安装
  8. Android系统名词解释汇总
  9. 解决官网下载Android Studio速度过慢
  10. 关于Android(安卓)Manifest中的