当我们在处理下载或是其他需要长时间执行的任务时,如果直接把处理函数放Activity的OnCreate或是OnStart中,会导致执行过程中整个Activity无响应,如果时间过长,程序还会挂掉。Handler就是把这些功能放到一个单独的线程里执行,与Activity互不影响。因此我们需要另外起动一个线程来处理长耗时操作,而主线程则不受其影响,在耗时操作完结发送消息给主线程,主线程再做相应处理。那么线程之间的消息传递和异步处理用的就是Handler。
Andrioid中费时的操作要放在子线程中处理,更新UI要放在主线程中进行。如果根据子线程的动作或进度要更新UI,在子线程中进行是不安全的,这时需要用到handler。Handler运行在主线程(UI线程)中,与子线程通过Message对象来传递数据。Handler接收子线程传过来的Message对象(子线程通过sendMessage()方法传递),把这些消息放入主线程队列中,配合主线程更新UI。
Handler的作用:安排消息或Runnable在某个主线程中的某个地方执行;安排一个动作在不同的线程中执行。
例1: 一个应用程序中有2个按钮(start、end),当点击start按钮时,执行一个线程,这个线程在控制台输出一串字符串,并且每隔3秒再执行一次线程,直到点击end按钮为止,线程停止。
步骤为:
1、 在Activity中,创建一个Handler对象
Handler handler = new Handler();
2、 在Activity中,创建一个Runnable对象
a) 以匿名内部类的方式
b) 将要执行的操作写在Runnable对象中的run()方法中
i. 打印出一句话
ii. 调用Runnable对象的postDelayed()方法
Runnable updateThread = new Runnable(){
//将要执行的操作写在线程对象的run方法当中
public void run(){
System.out.println("updateThread");
//调用Handler的postDelayed()方法 ,将要执行的线程对象放入到队列当中,待时间结束后,运行制定的线程对象
handler.postDelayed(updateThread, 3000);
}
};
3、 在Activity中,编写start按钮需要的监听器,并绑定
a) 在这个监听器的Onclick()方法中,调用Handler的post()方法,将要执行的线程对象放到队列当中。
handler.post(updateThread);
4、 在Activity中,编写end按钮需要的监听器,并帮定
a) 在这个监听器的Onclick()方法中,调用Handler的removeCallbacks ()方法,删除队列当中未执行的线程对象。
handler.removeCallbacks(updateThread);
例2:一个应用程序中有一个进度条和一个按钮,当点击按钮后,每隔一秒钟进度条前进一部分.
步骤为:
1、 创建线程对象,通过匿名内部类的方式进行声明。
i. 声明一个变量用来设置进度条的进度
ii. 重写线程类的run方法(),里面编写要执行的操作
a. 打印一个字符串;
b. 进度条的值增加;
c. 得到一个消息对象;
d. 设置消息对象arg1的值;
e. 让线程休眠一秒钟;
f. 将消息对象放入到消息队列中;
g. 判断,如果进度条的值等于100,则将线程对象从队列中移除。
Runnable updateThread = new Runnable(){
//将要执行的操作写在线程对象的run方法当中
public void run(){
System.out.println("updateThread");
//调用Handler的postDelayed()方法 ,将要执行的线程对象放入到队列当中,待时间结束后,运行制定的线程对象
handler.postDelayed(updateThread, 3000);
}
};
2、使用匿名内部类的方法生成Handler对象,重写Handler对象的handlerMessage(Message msg)方法。
i. 这个方法传入了一个Message对象,即消息对象,首先设置进度条的进度(这个值是Messag对象里面的一个成员变量arg1);
ii. 将要执行的线程对象放入到队列当中。
//使用匿名内部类来复写Handler当中的handlerMessage()方法
Handler updateBarHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
progressBar.setProgress(msg.arg1);
updateBarHandler.post(updateThread); //将要执行的线程放入到队列当中
}
};
注意: 使用Handler对象的post方法家在Runnable对象时不会新建线程,只是在当前线程中执行run方法。若要创建新的线程,需要使用Thread,将runnable对象作为参数传入。
Thread t = new Thread(r);
t.start();
为了使Activity和复杂的数据处理位于不同的线程中,可以使用HandlerThread,实现使用Looper来处理消息队列的功能,注意使用HandlerThread对象的getLooper方法之前,必须先调用该类的start方法。Looper在线程中运行一个消息循环,HandlerThread使用一个消息循环启用一个线程,而Handler用于处理消息。
Android中每一个Thread都跟着一个Looper,Looper可以帮助Thread维护一个消息队列,但是Looper和Handler没有什么关系。Android还提供了一个Thread继承类HanderThread可以帮助我们处理,在HandlerThread对象中可以通过getLooper方法获取一个Looper对象控制句柄,我们可以将其这个Looper对象映射到一个Handler中去来实现一个线程同步机制。
Messge对象的sendToTarget方法是将message发送到目标对象,所谓的目标对象就是生成改messgae对象的handler对象。
HandlerThread handlerThread = new HandlerThread("handlerThread");//生成handlerThread对象
handlerThread.start();//启动handlerThread对象
MyHandler myhandler = new MyHandler(handlerThread.getLooper());//使用handlerThread对象的looper对象作为参数,生成Myhandler对象(Handler的子类)。
Message msg = myhandler.obtainMessage();
msg.arg1 = 1;
msg.sendToTarget();
class MyHandler extends Handler{
public MyHandler(){
}
//编写以Looper对象为参数的构造函数
public MyHandler(Looper looper){
super(looper);
}
@Override //重写handleMessage方法
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
System.out,println(msg.arg1);
System.out.println("MyHandler---> id: " + Thread.currentThread().getId());
System.out.println("MyHandler---> name: " + Thread.currentThread().getName());
}
}