在非主线程中更新UI

在非主线程中调用了showMessage方法,结果报错:Can't create handler inside thread that has not called Looper.prepare()

  1. private void showMessage(String msg) { 
  2.         Toast toast = Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT); 
  3.         toast.setGravity(Gravity.CENTER, 0, 0); 
  4.         toast.show(); 
  5.     } 

原来Android中非主线程不能更新UI,Handler.post()方法可以解决这个问题:于是将showMessage方法稍作修改就可以了:

  1. private void showMessage(String msg) { 
  2.         mSg = msg; 
  3.         mHandler.post(new Runnable() { 
  4. @Override
  5. public void run() { 
  6.                   Toast toast = Toast.makeText(getApplicationContext(), mSg, Toast.LENGTH_SHORT); 
  7.                   toast.setGravity(Gravity.CENTER, 0, 0); 
  8.                   toast.show(); 
  9.             } 
  10.         }); 

 

 

对下面程序的一些个人看法:

   1:  package com.example.progressbardemo;
   2:   
   3:  import java.security.PublicKey;
   4:   
   5:  import android.os.Bundle;
   6:  import android.os.Handler;
   7:  import android.app.Activity;
   8:  import android.view.Menu;
   9:  import android.widget.ProgressBar;
  10:   
  11:  public class MainActivity extends Activity {
  12:      
  13:      
  14:      private ProgressBar mProgressBar;
  15:      private  int mProgressStatus = 0;
  16:      
  17:      //创建一个Handler对象
  18:      
  19:      private Handler mHandler = new Handler();
  20:      
  21:      @Override
  22:      protected void onCreate(Bundle savedInstanceState) {
  23:          super.onCreate(savedInstanceState);
  24:          setContentView(R.layout.activity_main);
  25:          
  26:          mProgressBar = (ProgressBar) findViewById(R.id.progressbar2);
  27:          //设定进度条的最大值,其将为该进度条显示的基数
  28:          mProgressBar.setMax(1000);
  29:          //新创建一个线程
  30:          new Thread( new Runnable() {
  31:              @Override
  32:              public void run() {
  33:                  // TODO Auto-generated method stub
  34:                  //循环1000次,不停更新mprogresstatus 的值
  35:                  while(mProgressStatus++<1000)
  36:                  {
  37:                      //将一个runnable对象添加到消息队列中,并且当执行到该对象时执行run方法
  38:                      mHandler.post(new Runnable() {  //http协议里面也有post,他的意思是将此线程里面的东西交给另一个线程处理
  39:                          
  40:                          @Override
  41:                          public void run() {
  42:                              // TODO Auto-generated method stub
  43:                              //重新设置进度条当前的值
  44:                              mProgressBar.setProgress(mProgressStatus);
  45:                          }
  46:                      });
  47:                  }
  48:              }
  49:          }).start();    
  50:      }
  51:   
  52:      @Override
  53:      public boolean onCreateOptionsMenu(Menu menu) {
  54:          // Inflate the menu; this adds items to the action bar if it is present.
  55:          getMenuInflater().inflate(R.menu.main, menu);
  56:          return true;
  57:      }
  58:   
  59:  }

 

public final boolean post (Runnable r) 
Since: API Level 1 
Causes the Runnable r to be added to the message queue. The runnable will be run on the thread to which this handler is attached.
 

    是不是可以这么理解:mhandle.post(Runnable r) 执行这条语句时,其实主线程UI已经与handle建立了联系,然后Runnable其实就是在主线程中运行的,只不过他是在主线程的消息队列当中运行的。这句话就好比在非主线程中调用handler的sendmessage()方法,然后在主线程中调用handlemessage()方法去处理截获到的消息。

    handler.post(Runnable  r) 中的Runnable 会在handler 所在的线程执行, 也就是View 所在的UI线程。这个就是所谓的线程间异步通信。也就是说,非主线程中可以调用主线程中的handle类以及方法去处理主线程的消息队列。也就是老师您说的候车室一样,主线程当中的handle可以处理消息队列中的很多消息。然后Looper一般默认存在在主线程中,他里面就是一个消息队列Looper可以控制消息队列的出与进。

    new Handler()默认的Looper是主线程的, //每个Looper里面都有一个消息队列
h = new Handler(Looper l)//l为主线程的Looper。此时h为l附属的handler,h的消息由post放在l的线程的队列中,由l所在的线程处理消息。(每个线程都有一个消息队列,h的消息由post放进I的线程所在的消息队列中去),这些也就是说明了,异步可以更新主线程UI。

参考这篇文章:

http://blog.sina.com.cn/s/blog_70677d110100s2kz.html

Thread + Handler方法:

具体是在需要重绘的地方调用handler的sendMessage方法发送消息,紧接着会os会触发handler中的handlerMessage方法,在handlerMessage方法中再调用view的invalidate或者postInvalidate方法就能实现重绘。
下面是我分别针对invalidate方法,给出view重绘代码,仅供参考:

Java代码 复制代码 收藏代码

  1. class CustomizeView extends WhichView { 
  2. public CustomizeView(Context context) { 
  3. super(context); 
  4. final Handler handler = new Handler(); 
  5. new Thread(new Runnable() { 
  6. @Override
  7. public void run() { 
  8. // delay some minutes you desire.
  9. handler.post(new Runnable() { 
  10. public void run() { 
  11. concreteUpdateUI(); 
  12. invalidate(); 
  13. }); 
  14. }).start(); 
  15. protected void concreteUpdateUI() { 
  16. // Add concrete movement for UI updates.
  17. // ...
  18. }

或者这样实现也可以。

Java代码 复制代码 收藏代码

  1. class CustomizeView extends TextView { 
  2. public CustomizeView(Context context) { 
  3. super(context); 
  4. new Thread(new UIUpdateThread()).start(); 
  5. class UIUpdateThread implements Runnable { 
  6. final Handler mHandler = new Handler(); 
  7. final Runnable mUpdateResults = new Runnable() { 
  8. public void run() { 
  9. concreteUpdateUI(); 
  10. invalidate(); 
  11. }; 
  12. public void run() { 
  13. // delay some minutes you desire.
  14. mHandler.post(mUpdateResults); 
  15. protected void concreteUpdateUI() { 
  16. // Add concrete movement for UI updates.
  17. // ...

其他方法:

如果你对于Android的Thread+Handler方式感觉繁琐,不妨试试Activity提供的另外一种简单的方法runOnUiThread,runOnUiThread可以帮助你在线程中执行UI更新操作,我们只需要在线程中写上类似

MyActivity .this.runOnUiThread(new Runnable() {

@Override

public void run() {

// refresh ui 的操作代码

}

});

这里需要注意的是runOnUiThread是Activity中的方法,在线程中我们需要告诉系统是哪个activity调用,所以前面显示的指明了activity。

posted on 2014-03-17 18:54  zhuxuekui3  阅读(660)  评论(0编辑  收藏  举报