Handler类的作用主要有两种:
1.在新启动的线程中发送消息。
2.在主线程(UI线程)中获取,处理消息。
注:主线程已经封装有Loop的消息队列处理机制,无需再创建。
Handler类包含如下方法用于消息发送,处理:
1.void handleMessage(Message msg):处理消息的方法。
2.final boolean hasMessages(int what):检查消息队列是否包含what的值。
3.final boolean hasMessages(int what, Object object):检查消息队列是否包含what的值且object为指定对象。
4.Message obtainMessage():获取消息。
5.sendEmptyMessage(int what):发送空消息。
6.final boolean sendemptyMessageDelayed(int what, long delayMillis):指定多少毫秒之后发送空消息。
7.final boolean sendMessage(Message msg):立即发送消息。
8.final boolean sendMessageDelayed(Message msg, long delayMillis):指定多少毫秒之后发送空消息。
demo:自动播放动画
下面代码实现是创建一个新线程来隔一定时间之后周期性的修改ImageView所显示的图片,实现一个动画效果。
public class HandlerActivity extends Activity { //定义周期性显示的图片ID int[] imageIds = new int[] { R.drawable.1, R.drawable.2, R.drawable.3, R.drawable.4, R.drawable.5 }; int currentImageId = 0; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); final ImageView show = (ImageView) findViewById(R.id.main_bt); final Handler myhHandler = new Handler() { @Override public void handleMessage(Message msg) { //如果该消息是本程序所发送的 if (msg.what == 0x12345) { //动态的修改所显示的图片 show.setImageResource(imageIds[currentImageId++]%imageIds.length); } } }; //定义一个计时器,让该计时器周期性的执行指定任务 new Timer().schedule(new TimerTask() { //TimerTask对象的本质是启动一个新线程 @Override public void run() { // TODO Auto-generated method stub //发送空消息 myhHandler.sendEmptyMessage(0x12345); } }, 0, 1200); } }
说明:当Timertask新线程发送消息时,位于主线程的handleMessage(Message msg)方法自动被回调,动态的修改ImagView组件的属性。效果:由新线程来周期性的修改ImageView的属性,从而实现动画效果。
Handler,Looper,MessageQueue的工作原理:
Looper:每个线程只有一个Looper,它负责管理MessageQueue,会不断的从MessageQueue中取出消息,并将消息分发给Handler处理。
MessageQueue:由Looper负责管理。它采用先进先出的方式来管理Message。
Handler:它能把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。
注:
public static final void prepare(){ if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); }
说明:prepare方法保证线程最多只有一个Looper对象。
public void loop(){ for (; ;) { Message msg = queue.next();//获取消息队列的下一个消息,如果没有消息,将会阻塞 if (msg == null) {//如果消息为null,表明消息队列正在退出 return; } Printer logging = me.mLogging; if (logging != null) { logging.println(""); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println(""); } //使用final修饰该标识符,保证在分发消息的过程中线程标识符不会被修改 final long newIdent = Binder.clearCallingIdentity(); if (iden != newIdent) { logging.println(""); } msg.recycle(); } }
说明:loop()方法使用一个死循环不断取出MessageQueue中的消息,并将取出的消息分给该消息对应的Handler进行处理。
在非主线程中使用Handler的步骤如下:
Demo:使用新线程计算质数
该实例允许用户输入一个数值上限,当用户单击“计算”按钮时,该应用会将该上限数值发送到新启动的线程,让该线程来计算该范围内的所有质数(之所以不直接在UI线程中计算该范围的所有质数,是因为UI线程需要响应用户动作,如果在UI线程中执行一个耗时操作,将会导致UI线程被阻塞,引起ANR异常)。
/* * 本实例在线程中创建一个Handler对象,然后UI线程的事件处理方法通过Handler向新线程发送消息。 */ public class CalPrime extends Activity{ 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>(); //计算从2开始,到upper的所有质数 outer: for (int i = 2; i <= upper; i++) { //用i除以从2开始,到i的平方根的所有数 for (int j = 2; j <= Math.sqrt(i); j++) { //如果可以整除,表明这个数不是质数 if (i != 2 && i % j == 0) { continue outer; } } nums.add(i); } //使用Toast显示统计出来的所有质数 Toast.makeText(CalPrime.this, nums.toString(), Toast.LENGTH_LONG).show(); } } }; Looper.loop(); } } } @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); etNum = (EditText)findViewById(R.id.etNum); CalThread 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); //向新线程中的Handler发送消息 calThread.mHandler.sendMessage(msg); }