原帖地址:http://blog.csdn.net/stonecao/article/details/6417364

在Android中谈到Handler,我们首先来讲一下Handler的机制和原理

1、Handler机制原理

Handler:主要是用来处理发送和接收消息的,作用是把消息加入特定的(Looper)消息队列中,并分发和处理该消息队列中的消息。构造Handler的时候可以指定一个Looper对象,通过Handler对象我们可以封装Message对象,然后通过sendMessage(msg)Message对象添加MessageQueue中;当MessageQueue循环到该Message时,就会调用该Message对象对应的handler对象的handleMessage()方法对其进行处理。

在主线程通过Handler handler = new Handler();即使用默认构造函数构造Handler时,是默认使用主线程的Looper对象,通过这种方式构造的Handler是属于主线程的,也就是Handler和Looper是绑定在一起的,如果构造Handler时指定一个新线程的Looper对象,则该Handler对象是属于该子线程的。

每个Android应用程序都运行在一个dalvik虚拟机进程中,进程开始的时候会启动一个主线程(MainThread),主线程负责处理和ui相关的事件,因此主线程通常又叫UI线程。而由于Android采用UI单线程模型,所以只能在主线程中对UI元素进行操作。如果在非UI线程直接对UI进行了操作,则会报错,Handler是需要在主线程中进行初始化的。为什么要这么说呢,因为你在自己new一个新线程中去简单建立一个Handler,程序执行是会报错的:

[java] view plain copy print ?
  1. java.lang.RuntimeException:Can'tcreatehandlerinsidethreadthathasnotcalledLooper.prepare()atandroid.os.Handler.<init>(Handler.java:121)atcom.cao.android.demos.handles.HandleTestActivity$MyThread$1.<init>(HandleTestActivity.java:86)atcom.cao.android.demos.handles.HandleTestActivity$MyThread.run(HandleTestActivity.java:86

为什么在主线程中不会报错,而在自己新建的线程中就会报这个错误呢?很简单,因为主线程它已经建立了Looper,因为主线程默认有一个自己的Looper对象和Message对象,非主线程默认是没有自己的Looper,需要自己去创建:通过Looper.prepare创建一个Looper对象,以及Looper.loop()创建消息循环队列在新线程(非主线程)中创建Handler对象,有两种方法实现

(1)创建Handler之前通过 Looper.prepare()方法创建Looper对象

[java] view plain copy print ?
  1. classMyThreadextendsThread{
  2. publicvoidrun(){
  3. Log.d(Constant.TAG,MessageFormat.format("Thread[{0}]--run...",Thread.currentThread().getName()));
  4. //非主线程中新建一个handler
  5. Looper.prepare();//创建该线程的Looper对象,用于接收消息,在非主线程中是没有looper的所以在创建handler前一定要使用prepare()创建一个Looper
  6. HandlermyThreadHandler=newHandler(){
  7. publicvoidhandleMessage(android.os.Messagemsg){
  8. Log.d(Constant.TAG,MessageFormat.format("Thread[{0}]--myThreadHandlerhandleMessagerun...",
  9. Thread.currentThread().getName()));
  10. }
  11. Looper.myLooper().loop();//建立一个消息循环,该线程不会退出
  12. };
  13. }
  14. }

(2)使用主线程的Looper对象,因为每个主线程默认已经创建了自己的Looper对象

[java] view plain copy print ?
  1. classMyThreadextendsThread{
  2. publicvoidrun(){
  3. Log.d(Constant.TAG,MessageFormat.format("Thread[{0}]--run...",Thread.currentThread().getName()));
  4. HandlerthreadMainLoopHandler=newHandler(Looper.getMainLooper()){
  5. //该handleMessage方法将在mainthread中执行
  6. publicvoidhandleMessage(android.os.Messagemsg){
  7. Log.d(Constant.Tag,MessageFormat.format("Thread[{0}]--threadMainLoopHandlerhandleMessagerun...",
  8. Thread.currentThread().getName()));
  9. }
  10. };
  11. }
  12. }

(3)小结

(1)和(2)的最主要区别在于:(1)的Handler对象属于子线程,其handleMessage是在子线程中执行,而(2)的Handler是属于主线程的,因为它和主线程的Looper.getMainLooper())对象绑定在一起的,所以在handleMessage方法中不要做复杂耗时的操作如网络、IO操作等,否则会阻塞UI线程,造成ANR现象。

熟悉了Handler、Looper机制的原理之后,接下来对于如何通过Handler来实现异步处理就更容易理解了,首先我们看一个通过Handler和Runnable来实现一个类似的计算器功能:

2、Handler+runable(可以实现类似一个计数器的功能,注意与下面3的区别,是同步还是异步方式呢?)

Handler对象在进行初始化的时候,会默认的自动绑定消息队列。利用类post方法,可以将Runnable对象发送到消息队列中,按照队列的机制按顺序执行不同的Runnable对象中的run方法。

[java] view plain copy print ?
  1. 1.publicclassHandlerActivityextendsActivity{
  2. 2./**Calledwhentheactivityisfirstcreated.*/
  3. 3.//声明两个按钮控件
  4. 4.privateButtonstartButton=null;
  5. 5.privateButtonendButton=null;
  6. 6.@Override
  7. 7.publicvoidonCreate(BundlesavedInstanceState){
  8. 8.super.onCreate(savedInstanceState);
  9. 9.setContentView(R.layout.main);
  10. 10.//根据控件的ID得到代表控件的对象,并未这两个按钮设置相应的监听器
  11. 11.startButton=(Button)findViewById(R.id.startButton);
  12. 12.startButton.setOnClickListener(newStartButtonListener());
  13. 13.endButton=(Button)findViewById(R.id.endButton);
  14. 14.endButton.setOnClickListener(newEndButtonListener());
  15. 15.
  16. 16.}
  17. 17.classStartButtonListenerimplementsOnClickListener{
  18. 18.
  19. 19.@Override
  20. 20.publicvoidonClick(Viewv){
  21. 21.//调用Handler的post方法,将要执行的线程对象添加到队列当中
  22. 22.handler.post(updateThread);
  23. 23.}
  24. 24.
  25. 25.}
  26. 26.
  27. 27.classEndButtonListenerimplementsOnClickListener{
  28. 28.
  29. 29.@Override
  30. 30.publicvoidonClick(Viewv){
  31. 31.handler.removeCallbacks(updateThread);
  32. 32.}
  33. 33.
  34. 34.}
  35. 35.//创建一个Handler对象
  36. 36.Handlerhandler=newHandler();
  37. 37.//将要执行的操作写在线程对象的run方法当中
  38. 38.RunnableupdateThread=newRunnable(){
  39. 39.
  40. 40.@Override
  41. 41.publicvoidrun(){
  42. 42.System.out.println("UpdateThread");
  43. 43.//在run方法内部,执行postDelayed或者是post方法
  44. 44.handler.postDelayed(updateThread,3000);
  45. 45.}
  46. 46.
  47. 47.};
  48. 48.}

3、通过Handler来实现异步处理的2种方式

3.1 采用Thread+ handler Message 方式

Thread+ handler Message 方式 即 :线程开启,采用Message传递后台(子)线程和UI主线程之间的信息,UI线程收到信息之后,进行更新UI的操作。

[java] view plain copy print ?
  1. publicclasshandlerTestActivityextendsActivity{
  2. privateProgressBarbar=null;
  3. privatebooleanisRunning=false;
  4. /*我们为这个Acivity创建一个用于和后台程序通信的handler,简单地,只要一收到message,就将progressbar进度增加5。*/
  5. /*步骤1:创建Handler,并通过handleMessage()给出当收到消息是UI需要进行如何处理,例子简单不对msg的内容进行分析*/
  6. Handlerhandler=newHandler(){
  7. publicvoidhandleMessage(Messagemsg){
  8. bar.incrementProgressBy(5);
  9. }
  10. };
  11. protectedvoidonCreate(BundlesavedInstanceState){
  12. super.onCreate(savedInstanceState);
  13. setContentView(R.layout.chapter_15_test1);
  14. bar=(ProgressBar)findViewById(R.id.c15_progress);
  15. }
  16. /*onStart是UI初始化并显示时调用*/
  17. protectedvoidonStart(){
  18. super.onStart();
  19. bar.setProgress(0);
  20. /*步骤2:建立后台线程处理,采用Thread,其中run()的内容,就是线程并行处理的内容,Thread是Runnable的implements*/
  21. Threadbackground=newThread(newRunnable(){
  22. publicvoidrun(){
  23. try{
  24. for(inti=0;i<20&&isRunning;i++){
  25. Thread.sleep(1000);
  26. /*步骤2.1:发送Message到队列中,参数中的obtainMessage()是用于给出一个新Message,本例无参数,对应的在handler在队列中收到这条消息时,则通过handleMessage()进行处理*/
  27. handler.sendMessage(handler.obtainMessage());
  28. }
  29. }catch(Throwablet){
  30. //jestendthethread
  31. }
  32. }
  33. });
  34. isRunning=true;
  35. /*步骤3:启动线程*/
  36. background.start();
  37. }
  38. /*onStop是UI停止显示时调用,例如我们按了返回键*/
  39. protectedvoidonStop(){
  40. super.onStop();
  41. isRunning=false;
  42. }
  43. }

3.2 Handler 和多线程处理方式,采用 Handler Post 、Runnable来实现(注意和2的区别)

Handler Post方法虽然发送的是一个实现了Runnable接口的类对象,但是它并非创建了一个新线程,而是执行了该对象中的run方法。也就是说,整个run中的操作和主线程处于同一个线程。

这样对于那些简单的操作,似乎并不会影响。但是对于耗时较长的操作,当它被加入到消息队列中之后执行会占用很长的时间,以至于处于同一线程的其他操作无法继续执行,就会出现“假死”。为了解决这个问题,就需要使得handler绑定到一个新开启线程的消息队列上,在这个处于另外线程的上的消息队列中处理传过来的Runnable对象和消息。SDK文档中也提供了相关说明:

[java] view plain copy print ?
  1. 1.publicclassHandlerTest2extendsActivity{
  2. 2.
  3. 3.@Override
  4. 4.protectedvoidonCreate(BundlesavedInstanceState){
  5. 5.//TODOAuto-generatedmethodstub
  6. 6.super.onCreate(savedInstanceState);
  7. 7.setContentView(R.layout.main);
  8. 8.//打印了当前线程的ID
  9. 9.System.out.println("Activity-->"+Thread.currentThread().getId());
  10. 10.
  11. 11.//生成一个HandlerThread对象
  12. 12.HandlerThreadhandlerThread=newHandlerThread("handler_thread");
  13. 13.//在使用HandlerThread的getLooper()方法之前,必须先调用该类的start(),同时开启一个新线程;
  14. 14.handlerThread.start();
  15. 15.//将由HandlerThread获取的Looper传递给Handler对象,即由处于另外线程的Looper代替handler初始化时默认绑定的消息队列来处理消息。
  16. 16.//自定义Handler
  17. 17.MyHandlermyHandler=newMyHandler(handlerThread.getLooper());
  18. 18.Messagemsg=myHandler.obtainMessage();
  19. 19.//将msg发送到目标对象,所谓的目标对象,就是生成该msg对象的handler对象
  20. 20.Bundleb=newBundle();
  21. 21.b.putInt("age",20);
  22. 22.b.putString("name","Jhon");
  23. 23.msg.setData(b);
  24. 24.msg.sendToTarget();//将msg发送到myHandler
  25. 25.}
  26. 26.
  27. 27.//自定义Handler类
  28. 28.classMyHandlerextendsHandler{
  29. 29.publicMyHandler(){
  30. 30.
  31. 31.}
  32. 32.
  33. 33.publicMyHandler(Looperlooper){
  34. 34.super(looper);
  35. 35.}
  36. 36.//这里的handleMessage将在子线程handlerThread中执行而不是在主线程中执行
  37. 37.@Override
  38. 38.publicvoidhandleMessage(Messagemsg){
  39. 39.Bundleb=msg.getData();
  40. 40.intage=b.getInt("age");
  41. 41.Stringname=b.getString("name");
  42. 42.System.out.println("ageis"+age+",nameis"+name);
  43. 43.System.out.println("Handler--->"+Thread.currentThread().getId());System.out.println("handlerMessage");
  44. 44.}
  45. 45.}
  46. 46.}

更多相关文章

  1. SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
  2. Android(安卓)设计模式之面向对象的六大原则
  3. android view只能在主线程操作
  4. “亲子安全卫士”项目总结
  5. 使用Android(安卓)BroadcastReceiver应该注意的一些问题
  6. WebService接口调试如此简单
  7. EditText的几种事件用法
  8. Android中xml文件的解析
  9. Camera框架初探

随机推荐

  1. 目前最全的android恶意程序的分类谱
  2. Android(安卓)Activity之间传递图片(Bitm
  3. Android中如何调用其它应用
  4. android 视频播放器的制作
  5. APP横竖屏不切换,不重走生命周期
  6. Android欢迎界面动画与跳转
  7. android-监听来电和去电
  8. Android(安卓)animation-list动画
  9. android相关技术文档汇总
  10. Android中WebView获取网页中标题 ,内容,