在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.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()    atandroid.os.Handler.<init>(Handler.java:121)at com.cao.android.demos.handles.HandleTestActivity$MyThread$1.<init>(HandleTestActivity.java:86)     at com.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对象

class MyThread extends Thread {   public void run() {           Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]-- run...", Thread .currentThread().getName()));       //非主线程中新建一个handler       Looper.prepare();// 创建该线程的Looper对象,用于接收消息,在非主线程中是没有looper的所以在创建handler前一定要使用prepare()创建一个Looper       Handler myThreadHandler = new Handler() {          public void handleMessage(android.os.Message msg) {       Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--myThreadHandler handleMessage run...",       Thread .currentThread().getName()));                                }        Looper.myLooper().loop();//建立一个消息循环,该线程不会退出                             };  } }

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

class MyThread extends Thread {   public void run() {           Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]-- run...", Thread .currentThread().getName()));       Handler threadMainLoopHandler = new Handler(Looper.getMainLooper()) {            //该handleMessage方法将在mainthread中执行              public void handleMessage(android.os.Message msg) {           Log.d(Constant.Tag,MessageFormat.format("Thread[{0}]--threadMainLoopHandler handleMessage run...",    Thread.currentThread().getName()));                                             }          };                 } }

(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方法。

1.public class HandlerActivity extends Activity {  2.    /** Called when the activity is first created. */ 3.    //声明两个按钮控件  4.    private Button startButton = null;  5.    private Button endButton = null;  6.    @Override 7.    public void onCreate(Bundle savedInstanceState) {  8.        super.onCreate(savedInstanceState);  9.        setContentView(R.layout.main);  10.        //根据控件的ID得到代表控件的对象,并未这两个按钮设置相应的监听器  11.        startButton = (Button)findViewById(R.id.startButton);  12.        startButton.setOnClickListener(new StartButtonListener());  13.        endButton = (Button)findViewById(R.id.endButton);  14.        endButton.setOnClickListener(new EndButtonListener());  15.          16.    }  17.    class StartButtonListener implements OnClickListener{  18. 19.        @Override 20.        public void onClick(View v) {  21.            //调用Handler的post方法,将要执行的线程对象添加到队列当中  22.            handler.post(updateThread);  23.        }  24.          25.    }  26.      27.    class EndButtonListener implements OnClickListener{  28. 29.        @Override 30.        public void onClick(View v) {  31.            handler.removeCallbacks(updateThread);  32.        }  33.          34.    }  35.    //创建一个Handler对象  36.    Handler handler  = new Handler();  37.    //将要执行的操作写在线程对象的run方法当中  38.    Runnable updateThread =  new Runnable(){  39. 40.        @Override 41.        public void run() {  42.            System.out.println("UpdateThread");  43.            //在run方法内部,执行postDelayed或者是post方法  44.            handler.postDelayed(updateThread, 3000);  45.        }  46.          47.    };  48.} 

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

3.1 采用Thread+ handler Message 方式

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

public class handlerTestActivity extends Activity{    private ProgressBar bar = null;    private boolean isRunning = false;        /* 我们为这个Acivity创建一个用于和后台程序通信的handler,简单地,只要一收到message,就将progressbar进度增加5。*/    /* 步骤1:创建Handler,并通过handleMessage()给出当收到消息是UI需要进行如何处理,例子简单不对msg的内容进行分析*/    Handler handler= new Handler(){        public void handleMessage(Message msg) {            bar.incrementProgressBy(5);        }    };        protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.chapter_15_test1);        bar=(ProgressBar)findViewById(R.id.c15_progress);    }    /*on Start是UI初始化并显示时调用*/    protected void onStart() {        super.onStart();        bar.setProgress(0);        /*步骤2:建立后台线程处理,采用Thread,其中run()的内容,就是线程并行处理的内容,Thread是Runnable的implements*/        Thread background = new Thread(new Runnable(){            public void run() {                try{                    for(int i = 0; i < 20 && isRunning; i ++){                        Thread.sleep(1000);                        /* 步骤2.1:发送Message到队列中,参数中的obtainMessage()是用于给出一个新Message,本例无参数,对应的在handler在队列中收到这条消息时,则通过handleMessage()进行处理*/                        handler.sendMessage(handler.obtainMessage());                    }                }catch(Throwable t){                    //jest end the thread                }            }                    });        isRunning = true;       /*步骤3:启动线程*/        background.start();    }    /*onStop是UI停止显示时调用,例如我们按了返回键*/    protected void onStop() {        super.onStop();        isRunning = false;    }  }

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

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

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

1.public class HandlerTest2 extends Activity {  2. 3.    @Override 4.    protected void onCreate(Bundle savedInstanceState) {  5.        // TODO Auto-generated method stub  6.    super.onCreate(savedInstanceState);  7.    setContentView(R.layout.main);  8.    //打印了当前线程的ID  9.    System.out.println("Activity-->" + Thread.currentThread().getId());  10.11.    //生成一个HandlerThread对象  12.    HandlerThread handlerThread = new HandlerThread("handler_thread");  13.    //在使用HandlerThread的getLooper()方法之前,必须先调用该类的start(),同时开启一个新线程;  14.    handlerThread.start();15.    //将由HandlerThread获取的Looper传递给Handler对象,即由处于另外线程的Looper代替handler初始化时默认绑定的消息队列来处理消息。  16.   //自定义Handler17.    MyHandler myHandler = new MyHandler(handlerThread.getLooper());  18.    Message msg = myHandler.obtainMessage();  19.    //将msg发送到目标对象,所谓的目标对象,就是生成该msg对象的handler对象  20.    Bundle b = new Bundle();  21.    b.putInt("age", 20);  22.    b.putString("name", "Jhon");  23.    msg.setData(b);  24.    msg.sendToTarget();//将msg发送到myHandler25.    }  26.      27.    //自定义Handler类28.    class MyHandler extends Handler{  29.    public MyHandler(){  30.              31.    }  32.    33.    public MyHandler(Looper looper){  34.        super(looper);  35.    }  36.    //这里的handleMessage将在子线程handlerThread中执行而不是在主线程中执行37.    @Override 38.    public void handleMessage(Message msg) {  39.        Bundle b = msg.getData();  40.        int age = b.getInt("age");  41.      String name = b.getString("name");  42.     System.out.println("age is " + age + ", name is" + name);  43.System.out.println("Handler--->" + Thread.currentThread().getId());          System.out.println("handlerMessage");  44.        }  45.    }  46.}  



更多相关文章

  1. Android(安卓)ANR问题分析
  2. 安卓动画
  3. Handler详解
  4. Android(安卓)性能优化
  5. http 请求时间
  6. Android面试计划指南
  7. android 实现listview动态加载列表项
  8. Android线程学习 一
  9. Android(安卓)Framework系列之IPC(二)

随机推荐

  1. 使用Android提供的android-support-v7出
  2. Android 根文件系统分析(1)
  3. How to install apk files on your googl
  4. 我会经常在这个地址下面共享自己保存的几
  5. Android RecyclerView实现九宫格效果
  6. Android TextView加载html图片详解
  7. 第一个PhoneGap程序以及错误解决
  8. Android学习——Activity生命周期
  9. 安装 Android 2.3 could not find adb.ex
  10. Android初级工程师面试题答案——Android