Handler类和Handler.Loop.MessageQueue的工作原理

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所显示的图片,实现一个动画效果。


    
    
  1. public class HandlerActivity extends Activity {
  2. //定义周期性显示的图片ID
  3. int[] imageIds = new int[]
  4. {
  5. R.drawable.1,
  6. R.drawable.2,
  7. R.drawable.3,
  8. R.drawable.4,
  9. R.drawable.5
  10. };
  11. int currentImageId = 0;
  12. @Override
  13. public void onCreate(Bundle savedInstanceState)
  14. {
  15. super.onCreate(savedInstanceState);
  16. setContentView(R.layout.main_activity);
  17. final ImageView show = (ImageView) findViewById(R.id.main_bt);
  18. final Handler myhHandler = new Handler()
  19. {
  20. @Override
  21. public void handleMessage(Message msg)
  22. {
  23. //如果该消息是本程序所发送的
  24. if (msg.what == 0x12345) {
  25. //动态的修改所显示的图片
  26. show.setImageResource(imageIds[currentImageId++]%imageIds.length);
  27. }
  28. }
  29. };
  30. //定义一个计时器,让该计时器周期性的执行指定任务
  31. new Timer().schedule( new TimerTask() { //TimerTask对象的本质是启动一个新线程
  32. @Override
  33. public void run() {
  34. // TODO Auto-generated method stub
  35. //发送空消息
  36. myhHandler.sendEmptyMessage( 0x12345);
  37. }
  38. }, 0, 1200);
  39. }
  40. }

说明:当Timertask新线程发送消息时,位于主线程的handleMessage(Message msg)方法自动被回调,动态的修改ImagView组件的属性。效果:由新线程来周期性的修改ImageView的属性,从而实现动画效果。

Handler,Looper,MessageQueue的工作原理:

Looper:每个线程只有一个Looper,它负责管理MessageQueue,会不断的从MessageQueue中取出消息,并将消息分发给Handler处理。

MessageQueue:由Looper负责管理。它采用先进先出的方式来管理Message。

Handler:它能把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。

注:

  • 在主线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,然后就可通过Handler来发送消息,处理消息。
  • 在自己启动的子线程,必须自己创建一个Looper对象,并启动它(分别调用prepare()和loop()方法),下面讲解这两个方法:

    
    
  1. public static final void prepare(){
  2. if (sThreadLocal.get() != null) {
  3. throw new RuntimeException( "Only one Looper may be created per thread");
  4. }
  5. sThreadLocal.set( new Looper());
  6. }

说明:prepare方法保证线程最多只有一个Looper对象。


    
    
  1. public void loop(){
  2. for (; ;) {
  3. Message msg = queue.next(); //获取消息队列的下一个消息,如果没有消息,将会阻塞
  4. if (msg == null) { //如果消息为null,表明消息队列正在退出
  5. return;
  6. }
  7. Printer logging = me.mLogging;
  8. if (logging != null) {
  9. logging.println( "");
  10. }
  11. msg.target.dispatchMessage(msg);
  12. if (logging != null) {
  13. logging.println( "");
  14. }
  15. //使用final修饰该标识符,保证在分发消息的过程中线程标识符不会被修改
  16. final long newIdent = Binder.clearCallingIdentity();
  17. if (iden != newIdent) {
  18. logging.println( "");
  19. }
  20. msg.recycle();
  21. }
  22. }

说明:loop()方法使用一个死循环不断取出MessageQueue中的消息,并将取出的消息分给该消息对应的Handler进行处理。
在非主线程中使用Handler的步骤如下:

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

Demo:使用新线程计算质数

该实例允许用户输入一个数值上限,当用户单击“计算”按钮时,该应用会将该上限数值发送到新启动的线程,让该线程来计算该范围内的所有质数(之所以不直接在UI线程中计算该范围的所有质数,是因为UI线程需要响应用户动作,如果在UI线程中执行一个耗时操作,将会导致UI线程被阻塞,引起ANR异常)。


    
    
  1. /*
  2. * 本实例在线程中创建一个Handler对象,然后UI线程的事件处理方法通过Handler向新线程发送消息。
  3. */
  4. public class CalPrime extends Activity{
  5. static final String UPPER_NUM = "upper";
  6. EditText etNum;
  7. CalThread calThread;
  8. class CalThread extends Thread{
  9. public Handler mHandler;
  10. public void run(){
  11. Looper.prepare();
  12. mHandler = new Handler(){
  13. @Override
  14. public void handleMessage(Message msg){
  15. if (msg.what == 0x123) {
  16. int upper = msg.getData().getInt(UPPER_NUM);
  17. List<Integer> nums = new ArrayList<Integer>();
  18. //计算从2开始,到upper的所有质数
  19. outer:
  20. for ( int i = 2; i <= upper; i++) {
  21. //用i除以从2开始,到i的平方根的所有数
  22. for ( int j = 2; j <= Math.sqrt(i); j++) {
  23. //如果可以整除,表明这个数不是质数
  24. if (i != 2 && i % j == 0) {
  25. continue outer;
  26. }
  27. }
  28. nums.add(i);
  29. }
  30. //使用Toast显示统计出来的所有质数
  31. Toast.makeText(CalPrime. this, nums.toString(), Toast.LENGTH_LONG).show();
  32. }
  33. }
  34. };
  35. Looper.loop();
  36. }
  37. }
  38. }
  39. @Override
  40. public void onCreate(Bundle savedInstanceState){
  41. super.onCreate(savedInstanceState);
  42. setContentView(R.layout.main_activity);
  43. etNum = (EditText)findViewById(R.id.etNum);
  44. CalThread calThread = new CalThread();
  45. calThread.start(); //启动新线程
  46. }
  47. //为按钮的点击事件提供事件处理函数
  48. public void cal(View source){
  49. //创建消息
  50. Message msg = new Message();
  51. msg.what = 0x123;
  52. Bundle bundle = new Bundle();
  53. bundle.putInt(UPPER_NUM, Integer.parseInt(etNum.getText().toString()));
  54. msg.setData(bundle);
  55. //向新线程中的Handler发送消息
  56. calThread.mHandler.sendMessage(msg);
  57. }



 

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所显示的图片,实现一个动画效果。


  
  
  1. public class HandlerActivity extends Activity {
  2. //定义周期性显示的图片ID
  3. int[] imageIds = new int[]
  4. {
  5. R.drawable.1,
  6. R.drawable.2,
  7. R.drawable.3,
  8. R.drawable.4,
  9. R.drawable.5
  10. };
  11. int currentImageId = 0;
  12. @Override
  13. public void onCreate(Bundle savedInstanceState)
  14. {
  15. super.onCreate(savedInstanceState);
  16. setContentView(R.layout.main_activity);
  17. final ImageView show = (ImageView) findViewById(R.id.main_bt);
  18. final Handler myhHandler = new Handler()
  19. {
  20. @Override
  21. public void handleMessage(Message msg)
  22. {
  23. //如果该消息是本程序所发送的
  24. if (msg.what == 0x12345) {
  25. //动态的修改所显示的图片
  26. show.setImageResource(imageIds[currentImageId++]%imageIds.length);
  27. }
  28. }
  29. };
  30. //定义一个计时器,让该计时器周期性的执行指定任务
  31. new Timer().schedule( new TimerTask() { //TimerTask对象的本质是启动一个新线程
  32. @Override
  33. public void run() {
  34. // TODO Auto-generated method stub
  35. //发送空消息
  36. myhHandler.sendEmptyMessage( 0x12345);
  37. }
  38. }, 0, 1200);
  39. }
  40. }

说明:当Timertask新线程发送消息时,位于主线程的handleMessage(Message msg)方法自动被回调,动态的修改ImagView组件的属性。效果:由新线程来周期性的修改ImageView的属性,从而实现动画效果。

Handler,Looper,MessageQueue的工作原理:

Looper:每个线程只有一个Looper,它负责管理MessageQueue,会不断的从MessageQueue中取出消息,并将消息分发给Handler处理。

MessageQueue:由Looper负责管理。它采用先进先出的方式来管理Message。

Handler:它能把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。

注:

  • 在主线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,然后就可通过Handler来发送消息,处理消息。
  • 在自己启动的子线程,必须自己创建一个Looper对象,并启动它(分别调用prepare()和loop()方法),下面讲解这两个方法:

  
  
  1. public static final void prepare(){
  2. if (sThreadLocal.get() != null) {
  3. throw new RuntimeException( "Only one Looper may be created per thread");
  4. }
  5. sThreadLocal.set( new Looper());
  6. }

说明:prepare方法保证线程最多只有一个Looper对象。


  
  
  1. public void loop(){
  2. for (; ;) {
  3. Message msg = queue.next(); //获取消息队列的下一个消息,如果没有消息,将会阻塞
  4. if (msg == null) { //如果消息为null,表明消息队列正在退出
  5. return;
  6. }
  7. Printer logging = me.mLogging;
  8. if (logging != null) {
  9. logging.println( "");
  10. }
  11. msg.target.dispatchMessage(msg);
  12. if (logging != null) {
  13. logging.println( "");
  14. }
  15. //使用final修饰该标识符,保证在分发消息的过程中线程标识符不会被修改
  16. final long newIdent = Binder.clearCallingIdentity();
  17. if (iden != newIdent) {
  18. logging.println( "");
  19. }
  20. msg.recycle();
  21. }
  22. }

说明:loop()方法使用一个死循环不断取出MessageQueue中的消息,并将取出的消息分给该消息对应的Handler进行处理。
在非主线程中使用Handler的步骤如下:

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

Demo:使用新线程计算质数

该实例允许用户输入一个数值上限,当用户单击“计算”按钮时,该应用会将该上限数值发送到新启动的线程,让该线程来计算该范围内的所有质数(之所以不直接在UI线程中计算该范围的所有质数,是因为UI线程需要响应用户动作,如果在UI线程中执行一个耗时操作,将会导致UI线程被阻塞,引起ANR异常)。


  
  
  1. /*
  2. * 本实例在线程中创建一个Handler对象,然后UI线程的事件处理方法通过Handler向新线程发送消息。
  3. */
  4. public class CalPrime extends Activity{
  5. static final String UPPER_NUM = "upper";
  6. EditText etNum;
  7. CalThread calThread;
  8. class CalThread extends Thread{
  9. public Handler mHandler;
  10. public void run(){
  11. Looper.prepare();
  12. mHandler = new Handler(){
  13. @Override
  14. public void handleMessage(Message msg){
  15. if (msg.what == 0x123) {
  16. int upper = msg.getData().getInt(UPPER_NUM);
  17. List<Integer> nums = new ArrayList<Integer>();
  18. //计算从2开始,到upper的所有质数
  19. outer:
  20. for ( int i = 2; i <= upper; i++) {
  21. //用i除以从2开始,到i的平方根的所有数
  22. for ( int j = 2; j <= Math.sqrt(i); j++) {
  23. //如果可以整除,表明这个数不是质数
  24. if (i != 2 && i % j == 0) {
  25. continue outer;
  26. }
  27. }
  28. nums.add(i);
  29. }
  30. //使用Toast显示统计出来的所有质数
  31. Toast.makeText(CalPrime. this, nums.toString(), Toast.LENGTH_LONG).show();
  32. }
  33. }
  34. };
  35. Looper.loop();
  36. }
  37. }
  38. }
  39. @Override
  40. public void onCreate(Bundle savedInstanceState){
  41. super.onCreate(savedInstanceState);
  42. setContentView(R.layout.main_activity);
  43. etNum = (EditText)findViewById(R.id.etNum);
  44. CalThread calThread = new CalThread();
  45. calThread.start(); //启动新线程
  46. }
  47. //为按钮的点击事件提供事件处理函数
  48. public void cal(View source){
  49. //创建消息
  50. Message msg = new Message();
  51. msg.what = 0x123;
  52. Bundle bundle = new Bundle();
  53. bundle.putInt(UPPER_NUM, Integer.parseInt(etNum.getText().toString()));
  54. msg.setData(bundle);
  55. //向新线程中的Handler发送消息
  56. calThread.mHandler.sendMessage(msg);
  57. }



 

猜你喜欢

转载自blog.csdn.net/zhaobinbin2015/article/details/80985438