Android的UI操作并不是线程安全的。Android消息传递机制是另一种形式的“事件处理”,Android只允许UI线程修改Activity里的UI组件,这样就会导致新启动的线程无法改变界面组件的属性值。这就需要Handler来实现




Handler的主要作用:


  • 在新线程中发送消息
  • 在主线程中获取、处理消息

为让主线程适时处理新线程所发送的消息,只能通过回调的方式来实现——开发者需要重写Handler类中处理消息的方法,当新线程发送消息时,消息会发送到与之关联的MessageQueue,而Handler会不断地从MessageQueue中获取并处理消息


Handler包含如下方法用于发送、处理消息:


void handleMessage(Message msg)

处理消息的方法

final boolean hasMessage(int what)

检查消息队列中是否包含what属性为指定值的消息

final boolean hasMessage(int what ,Object object)

what属性且object属性为制定对象的消息

多个重载的Message obtainMessage()

获取消息

sendEmptyMessage(int what)

发送空消息

final boolean sendMessageDelayed(int what, long delayMillis)

指定多少毫秒之后发送空消息

final boolean sendMessage(Message msg)

立即发送消息

final boolean sendMessageDelayed(Message msg, long delayMillis)

指定多少毫秒之后发送消息

示例代码:

public 
 class 
 MainActivity 
  extends 
 ActionBarActivity {

 
 int 
 [] 
 imageIds 
 = 
  new 
  int 
 []{
 R.drawable. 
 java 
 ,
 R.drawable. 
 ee 
 ,
 R.drawable. 
 ajax 
 ,
 R.drawable. 
 xml 
 ,
 R.drawable. 
 classic 
 
 };
 
 int 
 currentImageId 
 = 0;

     
  @Override 
 
     
  protected 
  void 
 onCreate(Bundle savedInstanceState) {
         
  super 
 .onCreate(savedInstanceState);
         setContentView(R.layout. 
 activity_main 
 );
         
  final 
 ImageView show = (ImageView) findViewById(R.id. 
 show 
 );
         
  final 
 Handler myHandler = 
 new 
 Handler()
         {
          
 @Override 
 
          
 public 
 void 
 handleMessage(Message msg)
         {
          
 if 
 (msg. 
 what 
 == 0x123)
         {
         show.setImageResource( 
 imageIds 
 [ 
 currentImageId 
 ++
                                       % 
  imageIds 
 . 
 length 
 ]);
         }
         }
         };
         
  new 
 Timer().schedule( 
 new 
 TimerTask()
         {
          
 @Override 
 
          
 public 
 void 
 run()
         {
         myHandler.sendEmptyMessage(0x123);
         }
         },0,1200); 
    }
 
}



3.5.2 Handler、Loop、MessageQueue的工作原理




Message

Handler接收和处理的消息对象

Looper

每个线程只能拥有一个Looper。负责读取MessageQueue中的消息

MessageQueue

消息队列,它采用先进先出的方式来管理Message。程序创建Looper对象时会在它的构造器中创建Looper对象

Looper提供的构造器源代码:

private Looper()
 
{
 
     mQueue = new MessageQueue();
 
     mRun = true; 

 

           mThread = Thread.currentThread(); 

 
}


程序在初始化Looper时会创建一个与之失联的MessageQueue。


  • Handler: 它的作用有两个——发送和处理消息。如果希望Handler工作,必须在当前线程中有一个Looper对象。为保证当前线程中有Looper对象,可分如下两种情况处理:
  1.      主UI线程中,系统已经初始化了一个Looper对象
  2. 程序员自己启动子线程,必须自己创建一个Looper对象,并启动它。创建Looper对象,调用它的prepare()方法即可
prepare()方法代码:
     
  public 
  static 
  final 
  void 
 prepare()
     {
      
 if 
 (sThreadLocal.get() != 
 null 
 )
     {
      
 throw 
 new 
 RuntimeException( 
 "Only one Looper may be created per thread" 
 );
     }
     sThreadLocal.set( 
 new 
 Looper()); 
    }


然后调用Looper的静态方法loop()方法来启动它。loop()方法使用一个死循环不断取MessageQueue中的消息,并将取出的消息对应的Handler进行处理。


loop()方法源代码:

for 
 (;;)
     {
     Message msg = queue.next();
      
 if 
 (msg == 
 null 
 )
     {
      
 return 
 ;
     }
     Printer logging = me.mLogging;
      
 if 
 (logging != 
 null 
 )
     {
     logging.println( 
 ">>>>>Dispatchging to " 
 + msg.target + 
  "" 
 
     msg.callback + 
  ": " 
 + msg.what);
     }
     msg.target.dispatchMessage(msg);
      
 if 
 (logging != newIdent)
     {
     Log.wtf(TAG, 
 "Thread identity changed from ox" 
 
     +Long.toHexString(ident)+ 
  "to 0x" 
 
     +Long.toHexString(newIdent)+ 
  "while dispatching to " 
 
     + msg.target.getClass().getName() + 
  " " 
 
     + msg.callback + 
  "what=" 
 + msg.what); 
    }
 
    msg.recycle();
 
    }


在线程中使用Handler的步骤如下:


  1. 调用Looper的prepare()方法为当前线程创建Looper对象,此时,构造器会创建与之配套的MessageQueue
  2. 有了Looper之后,创建Handler子类实例,重写handleMessage()方法,该方法负责处理来自于其他线程的消息
  3. 调用Looper的loop()方法启动Looper

尽量避免在UI线程中执行耗时操作,因为这样可能导致一个著名的异常:ANR异常,只要在UI线程中执行需要消耗大量时间的操作,都会引发ANR异常,因为这会导致Android应用程序无法响应输入事件和Broadcast


示例代码:

public 
 class 
 MainActivity 
  extends 
 ActionBarActivity {

 
 static 
 final 
 String 
  UPPER_NUM 
 = 
  "upper" 
 ;
 EditText 
  etNum 
 ;
 CalThread 
  calThread 
 ;
 
 class 
 CalThread 
  extends 
 Thread
 {
 
 public 
 Handler 
  mHandler 
 ;
 
 public 
 void 
 run()
 {
 Looper.prepare();
 
 mHandler 
 = 
  new 
 Handler()
 {
 
 @Override 
 
 
 public 
 void 
 handleMessage(Message msg)
 {
 
 if 
 (msg. 
 what 
 == 0x123)
 {
 
 int 
 upper = msg.getData().getInt( 
 UPPER_NUM 
 );
 List<Integer> nums = 
  new 
 ArrayList<Integer>();
 outer:
 
 for 
 ( 
 int 
 i = 2; i <= upper; i++)
 {
 
 for 
 ( 
 int 
 j = 2; j < Math.sqrt(i); j++)
 {
 
 if 
 (i != 2 && i % j == 0)
 {
 
 continue 
 outer;
 }
 }
 nums.add(i);
 }
 Toast.makeText(MainActivity. 
 this 
 , nums.toString(), Toast. 
 LENGTH_LONG 
 ).show();
 }
 }
 };
 Looper.loop();
 }
 }

     
  @Override 
 
     
  protected 
  void 
 onCreate(Bundle savedInstanceState) {
         
  super 
 .onCreate(savedInstanceState);
         setContentView(R.layout. 
 activity_main 
 );
         
  etNum 
 = (EditText) findViewById(R.id. 
 etNum 
 );
         
  calThread 
 = 
  new 
 CalThread();
         
  calThread 
 .start();
     }
     
  public 
  void 
 cal(View source)
     {
     Message msg = 
  new 
 Message();
     msg. 
 what 
 = 0x123;
     Bundle bundle = 
  new 
 Bundle();
     bundle.putInt( 
 UPPER_NUM 
 , Integer.parseInt( 
 etNum 
 .getText().toString()));
     msg.setData(bundle);
      
 calThread 
 . 
 mHandler 
 .sendMessage(msg); 
    }
 
}